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

使用QDataStream序列化/反序列化Eigen SparseMatrix编译错误求助

实现Eigen SparseMatrix的QDataStream反序列化问题解决

最终目标是使用QDataStream实现Eigen SparseMatrix的磁盘存读与压缩,但反序列化代码无法编译,尝试多种类型转换无效。以下是相关代码及问题分析与修复方案。

可编译的原序列化代码

// this overload compile
template <typename TempDI_, int whatever, typename IND>
inline QDataStream& operator <<(QDataStream &out, const Eigen::SparseMatrix<TempDI_, whatever, IND>& m)
{
    m.makeCompressed();
    IND rows, cols, nnzs, outS, innS;
    rows = m.rows()     ;
    cols = m.cols()     ;
    nnzs = m.nonZeros() ;
    outS = m.outerSize();
    innS = m.innerSize();

    out.writeBytes((const char *)&(rows), sizeof(IND));
    out.writeBytes((const char *)&(cols), sizeof(IND));
    out.writeBytes((const char *)&(nnzs), sizeof(IND));
    out.writeBytes((const char *)&(innS), sizeof(IND));
    out.writeBytes((const char *)&(outS), sizeof(IND));

    out.writeBytes((const char *)(m.valuePtr()),       sizeof(TempDI_  ) * m.nonZeros());
    out.writeBytes((const char *)(m.outerIndexPtr()),  sizeof(IND) * m.outerSize());
    out.writeBytes((const char *)(m.innerIndexPtr()),  sizeof(IND) * m.nonZeros());

    return out;
}

报错的原反序列化代码

// doesn't compile
template <typename TempDI_, int whatever, typename IND>
inline QDataStream &operator>>(QDataStream &in, Eigen::SparseMatrix<TempDI_, whatever, IND>& m)
{
    IND rows, cols, nnz, inSz, outSz;
    uint len;

    len= sizeof(IND);
    char* ind;
    in.readBytes(ind, len);
    rows = (IND)*ind;

    in.readBytes(ind, len);
    cols = (IND)*ind;

    in.readBytes(ind, len);
    nnz = (IND)*ind;

    in.readBytes(ind, len);
    inSz = (IND)*ind;

    in.readBytes(ind, len);
    outSz = (IND)*ind;

    m.resize(rows, cols);
    m.makeCompressed();
    m.resizeNonZeros(nnz);

    // compiling error :  error: cannot bind non-const lvalue reference of type 'char*&' to an rvalue of type 'char*'
    in.readBytes((char*)(m.valuePtr())     , sizeof(TempDI_  ) * nnz  );
    
    // compiling error :  error: invalid cast of an rvalue expression of type 'Eigen::SparseMatrix<int, 0, int>::StorageIndex*' {aka 'int*'} to type 'char*&'
    in.readBytes((char*&)(m.outerIndexPtr()), sizeof(IND) * outSz);

    // compiling error : error: cannot bind non-const lvalue reference of type 'char*&' to an rvalue of type 'char*'
    in.readBytes((char*)(m.innerIndexPtr()), sizeof(IND) * nnz );


    m.finalize();
}

标准库实现的可运行序列化/反序列化代码

template <typename TempDI, int whatever, typename IND>
    static void Serialize(QString path, Eigen::SparseMatrix<TempDI, whatever, IND>& m)
    {
        m.makeCompressed();

        std::fstream writeFile;
        writeFile.open(path.toStdString(), std::ios::binary | std::ios::out);

        if(writeFile.is_open())
        {
            IND rows, cols, nnzs, outS, innS;
            rows = m.rows()     ;
            cols = m.cols()     ;
            nnzs = m.nonZeros() ;
            outS = m.outerSize();
            innS = m.innerSize();

            writeFile.write((const char *)&(rows), sizeof(IND));
            writeFile.write((const char *)&(cols), sizeof(IND));
            writeFile.write((const char *)&(nnzs), sizeof(IND));
            writeFile.write((const char *)&(innS), sizeof(IND));
            writeFile.write((const char *)&(outS), sizeof(IND));

            writeFile.write((const char *)(m.valuePtr()),       sizeof(TempDI  ) * m.nonZeros());
            writeFile.write((const char *)(m.outerIndexPtr()),  sizeof(IND) * m.outerSize());
            writeFile.write((const char *)(m.innerIndexPtr()),  sizeof(IND) * m.nonZeros());

            writeFile.close();
        }

    };

    template <typename TempDI, int whatever, typename IND>
    static void Deserialize(QString path, Eigen::SparseMatrix<TempDI, whatever, IND>& m)
    {
        std::fstream readFile;
        readFile.open(path.toStdString(), std::ios::binary | std::ios::in);
        if(readFile.is_open())
        {
            IND rows, cols, nnz, inSz, outSz;
            readFile.read((char*)&rows , sizeof(IND));
            readFile.read((char*)&cols , sizeof(IND));
            readFile.read((char*)&nnz  , sizeof(IND));
            readFile.read((char*)&inSz , sizeof(IND));
            readFile.read((char*)&outSz, sizeof(IND));

            m.resize(rows, cols);
            m.makeCompressed();
            m.resizeNonZeros(nnz);

            readFile.read((char*)(m.valuePtr())     , sizeof(TempDI  ) * nnz  );
            readFile.read((char*)(m.outerIndexPtr()), sizeof(IND) * outSz);
            readFile.read((char*)(m.innerIndexPtr()), sizeof(IND) * nnz );

            m.finalize();

            readFile.close();
        }

         //qDebug()<<"Deserialize OK";
    };

错误原因分析

  1. QDataStream接口误用:readBytes的设计目的是读取带长度前缀的字节流,它会自动分配内存并将指针赋值给第一个参数(char*&类型的非const左值引用)。但你的场景是读取到已有的缓冲区(变量或矩阵内存),不需要自动分配,因此readBytes完全不适用,应该使用行为和std::fstream::read一致的readRawData。
  2. 未初始化指针风险:原反序列化代码中char* ind;未初始化就传入readBytes,会导致未定义行为,readBytes会尝试写入一个无效的指针地址。
  3. 非法类型转换:(char*&)(m.outerIndexPtr())这种强制转换是错误的,m.outerIndexPtr()返回的IND*转换为char*是临时右值,无法绑定到char*&类型的非const左值引用。
  4. 序列化与反序列化不匹配:原序列化代码用writeBytes会额外写入4字节的长度前缀,而标准库实现直接写入数据无前缀,导致两种序列化格式不兼容,同时反序列化如果用readRawData会读取到错误的数据。

修复后的代码

修正后的序列化代码

// 修正后,使用writeRawData避免额外长度前缀,和标准库行为一致
template <typename TempDI_, int whatever, typename IND>
inline QDataStream& operator <<(QDataStream &out, const Eigen::SparseMatrix<TempDI_, whatever, IND>& m)
{
    m.makeCompressed();
    IND rows = m.rows();
    IND cols = m.cols();
    IND nnzs = m.nonZeros();
    IND outS = m.outerSize();
    IND innS = m.innerSize();

    // 使用writeRawData直接写入原始数据,无额外长度前缀
    out.writeRawData(reinterpret_cast<const char*>(&rows), sizeof(IND));
    out.writeRawData(reinterpret_cast<const char*>(&cols), sizeof(IND));
    out.writeRawData(reinterpret_cast<const char*>(&nnzs), sizeof(IND));
    out.writeRawData(reinterpret_cast<const char*>(&innS), sizeof(IND));
    out.writeRawData(reinterpret_cast<const char*>(&outS), sizeof(IND));

    out.writeRawData(reinterpret_cast<const char*>(m.valuePtr()), sizeof(TempDI_) * m.nonZeros());
    out.writeRawData(reinterpret_cast<const char*>(m.outerIndexPtr()), sizeof(IND) * m.outerSize());
    out.writeRawData(reinterpret_cast<const char*>(m.innerIndexPtr()), sizeof(IND) * m.nonZeros());

    return out;
}

修复后的反序列化代码

// 修复后可正常编译运行
template <typename TempDI_, int whatever, typename IND>
inline QDataStream &operator>>(QDataStream &in, Eigen::SparseMatrix<TempDI_, whatever, IND>& m)
{
    IND rows, cols, nnz, inSz, outSz;

    // 使用readRawData直接读取到变量地址,无需分配内存
    in.readRawData(reinterpret_cast<char*>(&rows), sizeof(IND));
    in.readRawData(reinterpret_cast<char*>(&cols), sizeof(IND));
    in.readRawData(reinterpret_cast<char*>(&nnz), sizeof(IND));
    in.readRawData(reinterpret_cast<char*>(&inSz), sizeof(IND));
    in.readRawData(reinterpret_cast<char*>(&outSz), sizeof(IND));

    m.resize(rows, cols);
    m.makeCompressed();
    m.resizeNonZeros(nnz);

    // 使用readRawData读取到矩阵的内存区域,reinterpret_cast安全转换为char*
    in.readRawData(reinterpret_cast<char*>(m.valuePtr()), sizeof(TempDI_) * nnz);
    in.readRawData(reinterpret_cast<char*>(m.outerIndexPtr()), sizeof(IND) * outSz);
    in.readRawData(reinterpret_cast<char*>(m.innerIndexPtr()), sizeof(IND) * nnz);

    m.finalize();
    return in; // 必须返回QDataStream引用以支持链式调用
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 23:37:06