使用Boost.Python将Python的ndarray传入C++出现异常结果的问题
Boost.Python + NumPy: 解决ndarray数据处理的两个异常问题
我来帮你拆解这两个头疼的问题,都是因为对NumPy数组的类型内存布局理解不到位导致的,咱们一步步来解决:
先明确你的环境
- Ubuntu 16.04.6
- GCC 5.5.0
- Boost 1.58.0.1
- Python 2.7.12
- NumPy 1.14.0
问题1:get_data()转std::string无输出
原因分析
你犯了一个典型的类型错配错误:in.get_data()返回的是NumPy数组原始内存的指针,而你的数组是int类型(np.arange在64位系统默认生成int64)。把int数组的二进制内存直接当成char*转std::string,会出现两个问题:
- 第一个
int值是5,对应的ASCII是不可打印的控制字符,终端根本显示不出来; std::string是遇到\0才停止,但int数组里没有这个终止符,大概率会读到乱码或者直接无有效输出。
解决方法
如果你想打印数组内容,不要把int数组内存当字符串处理,直接遍历元素打印才是正确的姿势。如果确实需要把二进制数据转成std::string(比如做字节流传输),要明确按字节数拷贝:
// 按数组总字节数拷贝成二进制string(打印还是乱码,适合传输) std::string str(static_cast<char*>(in.get_data()), input_size * in.dtype().get_itemsize());
但日常调试用的话,直接遍历元素打印更靠谱:
std::cout << " === Array contents: "; for (int i = 0; i < input_size; ++i) { std::cout << boost::python::extract<int>(in[i]) << " "; } std::cout << std::endl;
问题2:转std::vector时部分值变0
原因分析
这个问题的核心是数据类型大小不匹配:
- 在Python 2.7的64位系统上,
np.arange()默认生成的是int64类型(每个元素占8字节); - 你在C++里用
reinterpret_cast<int*>把指针转成了32位int指针(GCC 5.x的int默认是4字节); - 这样每个8字节的
int64会被拆成两个4字节的int,比如5(0x0000000000000005)会被读成5和0,6会被读成6和0,以此类推,就出现了你看到的间隔0的情况。
另外,直接操作原始指针还忽略了NumPy数组的步长(stride),如果数组是非连续的(比如切片后的数组),这种写法会直接读错数据。
解决方法
有两种稳妥的方式:
- 统一类型大小:在Python创建数组时指定
dtype=np.int32,让数组元素和C++的int大小匹配:a = np.arange(5,10, dtype=np.int32) - 用Boost.Python的安全API:用
boost::python::extract来获取元素,不管数组的类型和步长,都能正确读取:std::vector<int> v(input_size); for (int i = 0; i < input_size; ++i) { // 自动做类型转换,安全可靠 v[i] = boost::python::extract<int>(in[i]); }
修改后的完整C++代码
#include <boost/python.hpp> #include <boost/numpy.hpp> #include <iostream> #include <string> #include <vector> char const* greet() { return "hello, world"; } boost::numpy::ndarray showArray(const boost::numpy::ndarray& in) { int input_size = in.shape(0); // 修复问题1:正确打印数组内容 std::cout << " === Array contents: "; for (int i = 0; i < input_size; ++i) { std::cout << boost::python::extract<int>(in[i]) << " "; } std::cout << std::endl; // 修复问题2:用extract安全读取元素 std::vector<int> v(input_size); for (int i = 0; i < input_size; ++i) { v[i] = boost::python::extract<int>(in[i]); } std::cout << "Vector elements:" << std::endl; for (auto itr : v) { std::cout << itr << std::endl; } // 构造输出数组(保持原有逻辑) int v_size = v.size(); boost::python::tuple shape = boost::python::make_tuple(v_size); boost::python::tuple stride = boost::python::make_tuple(sizeof(int)); boost::numpy::dtype dt = boost::numpy::dtype::get_builtin<int>(); boost::numpy::ndarray output = boost::numpy::from_data(&v[0], dt, shape, stride, boost::python::object()); boost::numpy::ndarray output_array = output.copy(); return output_array; } boost::numpy::ndarray printNDArray() { boost::python::tuple shape = boost::python::make_tuple(3,3,3); boost::numpy::dtype dtype = boost::numpy::dtype::get_builtin<float>(); boost::numpy::ndarray result = boost::numpy::zeros(shape,dtype); return result; } BOOST_PYTHON_MODULE(hello) { using namespace boost::python; Py_Initialize(); boost::numpy::initialize(); def("greet", greet); def("showArray", showArray); def("printndarray", printNDArray); }
修正后的终端输出
如果保持Python代码不变,用上面的C++代码编译运行,输出会变成:
[5 6 7 8 9] === Array contents: 5 6 7 8 9 Vector elements: 5 6 7 8 9 [5 6 7 8 9] hello, world [[[ 0. 0. 0.] [ 0. 0. 0.] [ 0. 0. 0.]] [[ 0. 0. 0.] [ 0. 0. 0.] [ 0. 0. 0.]] [[ 0. 0. 0.] [ 0. 0. 0.] [ 0. 0. 0.]]]
内容的提问来源于stack exchange,提问作者Frederic
相关产品推荐
相关产品推荐

