使用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"; };
错误原因分析
- QDataStream接口误用:
readBytes的设计目的是读取带长度前缀的字节流,它会自动分配内存并将指针赋值给第一个参数(char*&类型的非const左值引用)。但你的场景是读取到已有的缓冲区(变量或矩阵内存),不需要自动分配,因此readBytes完全不适用,应该使用行为和std::fstream::read一致的readRawData。 - 未初始化指针风险:原反序列化代码中
char* ind;未初始化就传入readBytes,会导致未定义行为,readBytes会尝试写入一个无效的指针地址。 - 非法类型转换:
(char*&)(m.outerIndexPtr())这种强制转换是错误的,m.outerIndexPtr()返回的IND*转换为char*是临时右值,无法绑定到char*&类型的非const左值引用。 - 序列化与反序列化不匹配:原序列化代码用
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
相关产品推荐
相关产品推荐

