Once I explained how to pass a python build-in object (dict, list, and
string) to C++ code using python.boost.
http://shitohichiumaya.blogspot.com/2010/08/boostpython-how-to-pass-python-object.html
This time, I will explain about how to pass a user defined python object
to C++ via python.boost. I will also explain why numpy.array's
numpy.int64 object casts an error when I do extract< int >. This casts a
TypeError (TypeError: No registered converter was able to produce a C++
rvalue of type int from this Python object of type numpy.int64()). Then
I will show you how to solve this problem.
Here is an python example code.
-----
#
# test pass object 2 module, python side implementation
# Copyright (C) 2012 Hitoshi
#
import numpy
import passobj2_mod
class TriMesh(object):
"""TriMesh: simplified triangle mesh primitive for python.boost
demo"""
def __init__(self, _mat_name):
"""default constructor."""
self.__material_name = _mat_name
self.vertex_list = []
self.face_idx_list = []
def get_material_name(self):
"""get material name
\return material name"""
return self.__material_name
def info_summary(self):
"""summary information
\return summary information string
"""
ret_str =\
'# materialname = ' + self.__material_name + '\n' +\
'# vertices = ' + str(len(self.vertex_list)) + '\n' +\
'# faces = ' + str(len(self.face_idx_list)) + '\n'
return ret_str
tmesh = TriMesh('diffuse_material')
# vertices
vlist = []
vlist.append(numpy.array([552.8, 0.0, 0.0]))
vlist.append(numpy.array([0.0, 0.0, 0.0]))
vlist.append(numpy.array([0.0, 0.0, 559.2]))
vlist.append(numpy.array([549.6, 0.0, 559.2]))
# faces (obj file like)
flist = []
flist.append(numpy.array([1, 2, 3]))
flist.append(numpy.array([1, 3, 4]))
tmesh.vertex_list = vlist
tmesh.face_idx_list = flist
print 'triangle mesh info\n' + tmesh.info_summary()
pobj2 = passobj2_mod.passobj2()
pobj2.pass_trimesh(tmesh)
-----
This code defines a simple triangle mesh class. Each triangle vertex is
represented by numpy.array that have three floats (a three-dimensional
vector). Each triangle face is represented by three vertex indices. You
may notice the index started with 1. Because I often use the wavefront
obj file format for a geometry file. In that file format, the index
started with 1. This TriMesh has a material name as a private member.
The C++ python.boost code that accesses to python TriMesh object is the
following.
-----
/// How to pass the python dict to the C++ function/method.
///
/// Copyright (C) 2010-2012 Hitoshi
///
/// test for using C++ class from python: pass an object that contains
/// list of numpy object
///
/// trimesh is an object, that has vertex list and face list.
#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;
//----------------------------------------------------------------------
/// print one numpy.array (actually length 3 sequence), type float
///
/// \param[in] float32_3_obj float[3] object
void print_float32_3(boost::python::object const & float32_3_obj)
{
// check sequence length is 3 or not.
if(boost::python::len(float32_3_obj) != 3){
std::string const objstr =
boost::python::extract< std::string >(
boost::python::str(float32_3_obj));
std::cerr << "print_float32_3: arg is not a float[3] obj ["
<< objstr << "]" << std::endl;
return;
}
float vec[] = {0.0f, 0.0f, 0.0f};
for(int i = 0; i < 3; ++i){
vec[i] = boost::python::extract< float >(
float32_3_obj.attr("__getitem__")(i));
}
std::cout << "float[3] = " << vec[0] << " " << vec[1] << " "
<< vec[2] << std::endl;
}
//----------------------------------------------------------------------
/// print one numpy.array (actually length 3 sequence), type int
///
/// \param[in] int32_3_obj float[3] object
void print_int32_3(boost::python::object const & int32_3_obj)
{
// check sequence length is 3 or not.
if(boost::python::len(int32_3_obj) != 3){
std::string const objstr =
boost::python::extract< std::string >(
boost::python::str(int32_3_obj));
std::cerr << "print_int32_3: arg is not a int[3] obj ["
<< objstr << "]" << std::endl;
return;
}
int vec[] = {0, 0, 0};
for(int i = 0; i < 3; ++i){
// MARK_03
object int64_obj = int32_3_obj.attr("__getitem__")(i);
vec[i] = boost::python::extract< int >(
int64_obj.attr("__int__")());
}
std::cout << "int[3] = " << vec[0] << " " << vec[1] << " "
<< vec[2] << std::endl;
}
//----------------------------------------------------------------------
/// accessing numpy.array list
///
/// \param[in] numpy_list_obj list of numpy.array.
/// \param[in] is_float true when float array
void access_numpy_array_list(object const & numpy_list_obj,
bool is_float)
{
int const len = boost::python::len(numpy_list_obj);
std::cout << "len(numpy_list_obj) = " << len << std::endl;
list cpp_list = extract< list >(numpy_list_obj);
for(int i = 0; i < len; ++i){
// operator[] returns python::boost::object.
object one_vec = cpp_list[i];
if(is_float){
print_float32_3(one_vec);
}
else{
print_int32_3(one_vec);
}
}
}
//----------------------------------------------------------------------
/// C++ object which python can instantiate
class PassObj2 {
public:
/// constructor
PassObj2()
{
// empty
}
/// pass a python object
/// \param[in] pytrimesh a trimesh object
void pass_trimesh(object const & pytrimesh) const {
// access to material name via the access method. MARK_01
std::string const matname =
extract< std::string >(pytrimesh.attr("get_material_name")());
std::cout << "trimesh.get_material_name() = " << matname << std::endl;
// access to python list
object const vlist_obj = pytrimesh.attr("vertex_list");
object const flist_obj = pytrimesh.attr("face_idx_list");
std::cout << "--- vertex_list ---" << std::endl;
access_numpy_array_list(vlist_obj, true);
std::cout << "--- face_idx_list ---" << std::endl;
access_numpy_array_list(flist_obj, false);
}
private:
};
/// importing module name is 'passobj_mod'
BOOST_PYTHON_MODULE(passobj2_mod)
{
class_
.def("pass_trimesh",
&PassObj2::pass_trimesh,
"pass python trimesh object to c++ method")
;
}
\end{verbatim}
The following code is a build script. Of course, you need python.boost
installed in your system.
\begin{verbatim}
#! /bin/sh -x
# Copyright (C) 2012 Hitoshi
#
PYTHON_INCLUDE=`python-config --includes`
MOD_CPP_SOURCE_BASE=passobj2_mod
g++ ${PYTHON_INCLUDE} -DPIC -shared -fPIC ${MOD_CPP_SOURCE_BASE}.cpp -o ${MOD_CPP_SOURCE_BASE}.so -lboost_python
\end{verbatim}
If you run this program, you will see the following result.
\begin{verbatim}
@[34]bash % python test_passobj2_mod.py
triangle mesh info
# materialname = diffuse_material
# vertices = 4
# faces = 2
trimesh.get_material_name() = diffuse_material
--- vertex_list ---
len(numpy_list_obj) = 4
float[3] = 552.8 0 0
float[3] = 0 0 0
float[3] = 0 0 559.2
float[3] = 549.6 0 559.2
--- face_idx_list ---
len(numpy_list_obj) = 2
int[3] = 1 2 3
int[3] = 1 3 4
---
The C++ output shows the same as in the python code.
This time I showed the code. Let's talk about the details in the
following blog entries.
Comments
class_("passobj2") should be class_("passobj2") as class_ is a class template.