自定义矩阵 + Boost::Serialization / C++17
Eigen Matrix + Boost::Serialization / C++17
我正在尝试为我们的代码库启用 C++17,它强烈基于 boost - 和 boost::serialization 用于中间数据存储和 pre-transmission 序列化。
总的来说,一切看起来都很好,似乎在工作,除了当我们序列化时 Eigen::Matrix objects 和 包括 boost 序列化支持 header 用于共享 ptr 序列化。
github 上的最小 example/testcode:https://github.com/nightsparc/EigenSerialize
[编辑] @Marc Glisse 在下方提供了一个简化的测试用例。参见:
我用不同的编译器(GCC6/7/8 和 Clang6)做了一些测试。我们通常使用 GCC 系统,它是 Ubuntu 18.04 的 GCC7.3。
对我来说,这似乎是与 GCC7 及更高版本的 C++17 模式相关的问题。
我的意思是,我没有在最小示例中使用 shared_ptr,所以我可以将其删除,一切都会好起来的......尽管如此,在我们的代码库中 shared_ptr 被序列化了无处不在。
你们中有人知道这里发生了什么吗?还是 GCC C++17 模式的错误?
测试代码(没有适当的错误处理和东西......):
#include <fstream>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/split_free.hpp>
#include <Eigen/Core>
// !! Conflicting include! Whenever the serialization wrapper for shared_ptrs is included
// the compilation fails!
// /usr/local/include/Eigen/src/Core/util/ForwardDeclarations.h:32:
// error: incomplete type ‘Eigen::internal::traits<boost::serialization::U>’ used in nested name specifier
// enum { has_direct_access = (traits<Derived>::Flags & DirectAccessBit) ? 1 : 0,
#include <boost/serialization/shared_ptr.hpp>
// Serialization methods for fixed-size Eigen::Matrix type
namespace boost {
namespace serialization {
template<
class Archive,
typename _Scalar,
int _Rows,
int _Cols,
int _Options,
int _MaxRows,
int _MaxCols
>
inline void serialize(Archive & arArchive,
Eigen::Matrix<_Scalar,
_Rows,
_Cols,
_Options,
_MaxRows,
_MaxCols> & arMatrix,
const unsigned int aVersion)
{
boost::serialization::split_free(arArchive, arMatrix, aVersion);
}
template<
class Archive,
typename _Scalar,
int _Rows,
int _Cols,
int _Options,
int _MaxRows,
int _MaxCols
>
inline void save(Archive & arArchive,
const Eigen::Matrix<_Scalar,
_Rows,
_Cols,
_Options,
_MaxRows,
_MaxCols> & arMatrix,
const unsigned int)
{
typedef typename Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>::Index TEigenIndex;
const TEigenIndex lRows = arMatrix.rows();
const TEigenIndex lCols = arMatrix.cols();
arArchive << lRows;
arArchive << lCols;
if(lRows > 0 && lCols > 0)
{
arArchive & boost::serialization::make_array(arMatrix.data(), arMatrix.size());
}
}
template<
class Archive,
typename _Scalar,
int _Rows,
int _Cols,
int _Options,
int _MaxRows,
int _MaxCols
>
inline void load(Archive & arArchive,
Eigen::Matrix<_Scalar,
_Rows,
_Cols,
_Options,
_MaxRows,
_MaxCols> & arMatrix,
const unsigned int)
{
typedef typename Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>::Index TEigenIndex;
TEigenIndex lRows, lCols;
// deserialize meta data
arArchive & lRows;
arArchive & lCols;
// do some error handling here
if(lRows > 0 && lCols > 0)
{
// deserialize data
arArchive & boost::serialization::make_array(arMatrix.data(), arMatrix.size());
}
}
}
}
class TestClass
{
public:
TestClass()
{
// fill eigen
m(0,0) = 3;
m(1,0) = 2.5;
m(0,1) = -1;
m(1,1) = m(1,0) + m(0,1);
}
private:
friend class boost::serialization::access;
Eigen::Matrix2d m;
template<class Archive>
void serialize(Archive &ar, const unsigned int)
{
ar & m;
}
};
int main(void)
{
using namespace boost::archive;
// Serialize
TestClass TestA;
std::ofstream oss("test.log");
{
text_oarchive oa(oss);
oa << TestA;
}
// deserialize now
TestClass TestB;
std::ifstream iss("test.log");
{
text_iarchive ia(iss);
ia >> TestB;
}
}
[编辑 2019-02-06]
GCC-Bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84075
Eigen-Bug: http://eigen.tuxfamily.org/bz/show_bug.cgi?id=1676
[编辑 2019-02-07]
提高公关:https://github.com/boostorg/serialization/pull/144
不确定错误,但为什么需要 boost::serialization::split_free
而不是简单地这样做:
// Serialization methods for fixed or dynamic-size Eigen::Matrix type
namespace boost {namespace serialization {
template<class Archive, typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols>
inline void serialize(Archive & ar,
Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> & matrix,
const unsigned int /* aVersion */)
{
Eigen::Index rows = matrix.rows();
Eigen::Index cols = matrix.cols();
ar & (rows);
ar & (cols);
if(rows != matrix.rows() || cols != matrix.cols())
matrix.resize(rows, cols);
if(matrix.size() !=0)
ar & boost::serialization::make_array(matrix.data(), rows * cols);
}
} } // namespace boost::serialization
对我来说,使用带有 boost 1.58 的 C++17 以及 clang 5/6 和 gcc 6/7/8 上的最新 Eigen3.3 或 Eigen-default 版本对我来说效果很好。
我添加了一个 matrix.resize()
,它应该使代码也适用于动态矩阵,对于固定大小的矩阵,这应该不会引入任何开销(当使用优化编译时)——实际上它应该在读取非- 可调整大小的矩阵(在没有 -DNDEBUG
的情况下编译)。
如果您想保留当前基于 split_free
的序列化,您可以通过添加此模板专业化来解决此问题。它需要在包含 Eigen/Core
之后的某处,在声明您的序列化之前,是否包含 <boost/serialization/shared_ptr.hpp>
并不重要。
namespace boost { namespace serialization {
struct U; // forward-declaration for Bug 1676
} } // boost::serialization
namespace Eigen { namespace internal {
// Workaround for bug 1676
template<>
struct traits<boost::serialization::U> {enum {Flags=0};};
} }
我猜测(不可靠)C++14 和 C++17 之间的差异是由于模板参数推导(编译器不能仅仅因为参数数量太少而忽略模板),即gcc 不作为 SFINAE 处理。我不知道这个错误是在 gcc 还是在 Eigen 中,但无论如何这里有一个更简化的测试用例
#include <Eigen/Core>
template<template<class U>class SPT>void f(SPT<class U>&);
template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols>
void f(Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> & arMatrix){}
int main()
{
Eigen::Matrix2d m;
f(m);
}
我正在尝试为我们的代码库启用 C++17,它强烈基于 boost - 和 boost::serialization 用于中间数据存储和 pre-transmission 序列化。
总的来说,一切看起来都很好,似乎在工作,除了当我们序列化时 Eigen::Matrix objects 和 包括 boost 序列化支持 header 用于共享 ptr 序列化。
github 上的最小 example/testcode:https://github.com/nightsparc/EigenSerialize
[编辑] @Marc Glisse 在下方提供了一个简化的测试用例。参见:
我用不同的编译器(GCC6/7/8 和 Clang6)做了一些测试。我们通常使用 GCC 系统,它是 Ubuntu 18.04 的 GCC7.3。 对我来说,这似乎是与 GCC7 及更高版本的 C++17 模式相关的问题。
我的意思是,我没有在最小示例中使用 shared_ptr,所以我可以将其删除,一切都会好起来的......尽管如此,在我们的代码库中 shared_ptr 被序列化了无处不在。
你们中有人知道这里发生了什么吗?还是 GCC C++17 模式的错误?
测试代码(没有适当的错误处理和东西......):
#include <fstream>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/split_free.hpp>
#include <Eigen/Core>
// !! Conflicting include! Whenever the serialization wrapper for shared_ptrs is included
// the compilation fails!
// /usr/local/include/Eigen/src/Core/util/ForwardDeclarations.h:32:
// error: incomplete type ‘Eigen::internal::traits<boost::serialization::U>’ used in nested name specifier
// enum { has_direct_access = (traits<Derived>::Flags & DirectAccessBit) ? 1 : 0,
#include <boost/serialization/shared_ptr.hpp>
// Serialization methods for fixed-size Eigen::Matrix type
namespace boost {
namespace serialization {
template<
class Archive,
typename _Scalar,
int _Rows,
int _Cols,
int _Options,
int _MaxRows,
int _MaxCols
>
inline void serialize(Archive & arArchive,
Eigen::Matrix<_Scalar,
_Rows,
_Cols,
_Options,
_MaxRows,
_MaxCols> & arMatrix,
const unsigned int aVersion)
{
boost::serialization::split_free(arArchive, arMatrix, aVersion);
}
template<
class Archive,
typename _Scalar,
int _Rows,
int _Cols,
int _Options,
int _MaxRows,
int _MaxCols
>
inline void save(Archive & arArchive,
const Eigen::Matrix<_Scalar,
_Rows,
_Cols,
_Options,
_MaxRows,
_MaxCols> & arMatrix,
const unsigned int)
{
typedef typename Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>::Index TEigenIndex;
const TEigenIndex lRows = arMatrix.rows();
const TEigenIndex lCols = arMatrix.cols();
arArchive << lRows;
arArchive << lCols;
if(lRows > 0 && lCols > 0)
{
arArchive & boost::serialization::make_array(arMatrix.data(), arMatrix.size());
}
}
template<
class Archive,
typename _Scalar,
int _Rows,
int _Cols,
int _Options,
int _MaxRows,
int _MaxCols
>
inline void load(Archive & arArchive,
Eigen::Matrix<_Scalar,
_Rows,
_Cols,
_Options,
_MaxRows,
_MaxCols> & arMatrix,
const unsigned int)
{
typedef typename Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>::Index TEigenIndex;
TEigenIndex lRows, lCols;
// deserialize meta data
arArchive & lRows;
arArchive & lCols;
// do some error handling here
if(lRows > 0 && lCols > 0)
{
// deserialize data
arArchive & boost::serialization::make_array(arMatrix.data(), arMatrix.size());
}
}
}
}
class TestClass
{
public:
TestClass()
{
// fill eigen
m(0,0) = 3;
m(1,0) = 2.5;
m(0,1) = -1;
m(1,1) = m(1,0) + m(0,1);
}
private:
friend class boost::serialization::access;
Eigen::Matrix2d m;
template<class Archive>
void serialize(Archive &ar, const unsigned int)
{
ar & m;
}
};
int main(void)
{
using namespace boost::archive;
// Serialize
TestClass TestA;
std::ofstream oss("test.log");
{
text_oarchive oa(oss);
oa << TestA;
}
// deserialize now
TestClass TestB;
std::ifstream iss("test.log");
{
text_iarchive ia(iss);
ia >> TestB;
}
}
[编辑 2019-02-06]
GCC-Bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84075
Eigen-Bug: http://eigen.tuxfamily.org/bz/show_bug.cgi?id=1676
[编辑 2019-02-07]
提高公关:https://github.com/boostorg/serialization/pull/144
不确定错误,但为什么需要 boost::serialization::split_free
而不是简单地这样做:
// Serialization methods for fixed or dynamic-size Eigen::Matrix type
namespace boost {namespace serialization {
template<class Archive, typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols>
inline void serialize(Archive & ar,
Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> & matrix,
const unsigned int /* aVersion */)
{
Eigen::Index rows = matrix.rows();
Eigen::Index cols = matrix.cols();
ar & (rows);
ar & (cols);
if(rows != matrix.rows() || cols != matrix.cols())
matrix.resize(rows, cols);
if(matrix.size() !=0)
ar & boost::serialization::make_array(matrix.data(), rows * cols);
}
} } // namespace boost::serialization
对我来说,使用带有 boost 1.58 的 C++17 以及 clang 5/6 和 gcc 6/7/8 上的最新 Eigen3.3 或 Eigen-default 版本对我来说效果很好。
我添加了一个 matrix.resize()
,它应该使代码也适用于动态矩阵,对于固定大小的矩阵,这应该不会引入任何开销(当使用优化编译时)——实际上它应该在读取非- 可调整大小的矩阵(在没有 -DNDEBUG
的情况下编译)。
如果您想保留当前基于 split_free
的序列化,您可以通过添加此模板专业化来解决此问题。它需要在包含 Eigen/Core
之后的某处,在声明您的序列化之前,是否包含 <boost/serialization/shared_ptr.hpp>
并不重要。
namespace boost { namespace serialization {
struct U; // forward-declaration for Bug 1676
} } // boost::serialization
namespace Eigen { namespace internal {
// Workaround for bug 1676
template<>
struct traits<boost::serialization::U> {enum {Flags=0};};
} }
我猜测(不可靠)C++14 和 C++17 之间的差异是由于模板参数推导(编译器不能仅仅因为参数数量太少而忽略模板),即gcc 不作为 SFINAE 处理。我不知道这个错误是在 gcc 还是在 Eigen 中,但无论如何这里有一个更简化的测试用例
#include <Eigen/Core>
template<template<class U>class SPT>void f(SPT<class U>&);
template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols>
void f(Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> & arMatrix){}
int main()
{
Eigen::Matrix2d m;
f(m);
}