You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++中生成可被Python pickle加载为numpy ndarray的字节数据

解决方案:C++生成可被Python pickle加载的numpy ndarray字节数据

问题概述

需要在C++中完成两个核心步骤:

  1. 将GSL的gsl_vector和gsl_matrix转换为C++的ndarray结构;
  2. 将该ndarray序列化为字节数据,确保Python的pickle.loads能直接识别为numpy ndarray。

无需跨库的简化方案

方案1:用Boost.Python实现完整流程

Boost.Python原生提供ndarray实现和pickle序列化支持,无需依赖其他库,是最直接的解决方案:

步骤1:GSL容器转Boost ndarray

#include <boost/python/numpy.hpp>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <algorithm>

namespace np = boost::python::numpy;

// gsl_vector转Boost ndarray
np::array gsl_vector_to_ndarray(const gsl_vector* vec) {
    np::dtype dtype = np::dtype::get_builtin<double>();
    np::array arr = np::empty(vec->size, dtype);
    std::copy(vec->data, vec->data + vec->size, reinterpret_cast<double*>(arr.get_data()));
    return arr;
}

// gsl_matrix转Boost ndarray
np::array gsl_matrix_to_ndarray(const gsl_matrix* mat) {
    np::dtype dtype = np::dtype::get_builtin<double>();
    boost::python::tuple shape = boost::python::make_tuple(mat->size1, mat->size2);
    np::array arr = np::empty(shape, dtype);
    
    double* arr_data = reinterpret_cast<double*>(arr.get_data());
    for (size_t i = 0; i < mat->size1; ++i) {
        std::copy(gsl_matrix_row(mat, i)->data, 
                  gsl_matrix_row(mat, i)->data + mat->size2, 
                  arr_data + i * mat->size2);
    }
    return arr;
}

步骤2:Boost ndarray序列化为pickle字节流

#include <boost/python.hpp>
#include <vector>
#include <string>

std::vector<char> ndarray_to_pickle_bytes(np::array arr) {
    boost::python::object pickle = boost::python::import("pickle");
    boost::python::object dumps = pickle.attr("dumps");
    // 调用pickle.dumps,使用最高兼容协议版本
    boost::python::str pickle_str = dumps(arr, boost::python::object(-1));
    
    std::string str = boost::python::extract<std::string>(pickle_str);
    return std::vector<char>(str.begin(), str.end());
}

方案2:手动构造numpy pickle格式(无第三方库依赖)

如果不想依赖Boost,可以直接按照numpy的pickle协议构造字节数据,核心是存储数组的** dtype、形状、数据缓冲区**及numpy类标识:

#include <vector>
#include <cstdint>

std::vector<char> manual_ndarray_to_pickle(const double* data, size_t rows, size_t cols) {
    std::vector<char> pickle_data;

    // Pickle协议头(协议4,兼容Python3.4+)
    pickle_data.push_back(0x80);
    pickle_data.push_back(0x04);

    // 引用numpy.ndarray类
    pickle_data.insert(pickle_data.end(), "cnumpy.core.multiarray\nndarray\nq\x00", 29);
    // 构造__reduce__返回的元组结构
    pickle_data.push_back(0x85);
    pickle_data.insert(pickle_data.end(), "q\x01cnumpy.core.multiarray\n_reconstruct\nq\x02", 36);
    pickle_data.push_back(0x8c);
    pickle_data.push_back(0x01);
    pickle_data.push_back(0x94);

    // 写入形状元组
    pickle_data.push_back(0x85);
    pickle_data.insert(pickle_data.end(), "K", 1); pickle_data.push_back(rows);
    pickle_data.insert(pickle_data.end(), "K", 1); pickle_data.push_back(cols);
    pickle_data.push_back(0x94);

    // 写入dtype(float64小端)
    pickle_data.insert(pickle_data.end(), "X\x08\x00\x00\x00<f8\x94", 8);
    // 写入存储顺序(C行优先)
    pickle_data.insert(pickle_data.end(), "X\x01\x00\x00\x00C\x94", 6);
    // 写入原始数据
    const char* data_bytes = reinterpret_cast<const char*>(data);
    pickle_data.insert(pickle_data.end(), data_bytes, data_bytes + rows*cols*sizeof(double));

    // 结束标记
    pickle_data.push_back(0x94);
    pickle_data.push_back(0x87);
    pickle_data.push_back(0x94);
    pickle_data.push_back(0x2e);

    return pickle_data;
}

关于Boost转PicklingTools Val的说明

如果一定要用PicklingTools,需要手动将Boost ndarray的属性映射为Val结构:创建DictVal模拟numpy.ndarray的shape(TupleVal)、dtype(StrVal)、data(BytesVal)等关键属性,再用DumpValToArray序列化。但这种方式需要额外处理numpy类的标识映射,步骤繁琐且容易出错,完全没必要,优先推荐前两种方案。

内容的提问来源于stack exchange,提问作者FV_at_Alst

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 16:25:02