【问题标题】:Eigen Matrix + Boost::Serialization / C++17特征矩阵 + Boost::序列化 / C++17
【发布时间】:2019-06-29 06:00:48
【问题描述】:

我正在尝试为我们的代码库启用 C++17,该代码库强烈基于 boost - 和 boost::serialization 用于中间数据存储和预传输序列化。

总的来说,一切看起来都很好,似乎工作正常,除非我们序列化 Eigen::Matrix 对象并且包含用于共享 ptr 序列化的 boost 序列化支持标头。

github 上的最小示例/测试代码:https://github.com/nightsparc/EigenSerialize

[编辑] @Marc Glisse 在下面提供了一个简化的测试用例。见:https://stackoverflow.com/a/54536756/1267320

我用不同的编译器(GCC6/7/8 和 Clang6)做了一些测试。我们通常使用系统 GCC,即 Ubuntu 18.04 的 GCC7.3。 对我来说,这似乎是与 GCC7 及更高版本的 C++17 模式有关的问题。

我的意思是,我没有在最小示例中使用 shared_ptr,所以我可以删除它,一切都会好起来的......尽管如此,在我们的代码库中,shared_ptr 到处都被序列化了。

你们中有人知道这里发生了什么吗?还是 GCC C++17 模式下的 bug?

测试代码(没有适当的错误处理和东西......):

#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 错误:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84075
特征错误:http://eigen.tuxfamily.org/bz/show_bug.cgi?id=1676

[编辑 2019-02-07]
提升公关:https://github.com/boostorg/serialization/pull/144

【问题讨论】:

  • using namespace std; using namespace boost; 现在你真的在要求它,不是吗?没有它们,问题仍然存在,因此最好将它们从您的问题中删除。
  • 问题不在于 shared_ptr,只是恰好有一个声明 namespace boost{namespace serialization{template&lt;class Archive,template&lt;class U&gt;class SPT&gt;void load(Archive&amp;ar,SPT&lt;class U&gt;&amp;t,const unsigned int file_version);}},所以它可以被标记为朋友,这会以某种方式破坏事情。
  • 你说得对...我删除了 using 指令。并感谢下面的最小示例
  • boost-serialization 中的前向声明实际上没什么意义。我做了一个 PR 来解决这个问题(并添加了一个链接)

标签: c++ gcc boost eigen boost-serialization


【解决方案1】:

不确定错误,但为什么需要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

在 C++17 和 boost 1.58 以及最新的 Eigen3.3 或 Clang 5/6 和 gcc 6/7/8 上的 Eigen-default 版本上工作正常。

我添加了一个matrix.resize(),它应该使代码也适用于动态矩阵,对于固定大小的矩阵,这应该不会引入开销(当使用优化编译时)——实际上它应该在读取不可调整大小的矩阵时断言(编译时没有-DNDEBUG)。


如果您想保留当前基于split_free 的序列化,您可以通过添加此模板专业化来解决此问题。它需要在包含Eigen/Core之后的某个地方,在声明您的序列化之前,是否包含&lt;boost/serialization/shared_ptr.hpp&gt;都没有关系。

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};};
} } 

【讨论】:

  • 基本上,我在生产代码中使用 split_free 来区分固定大小和动态大小的矩阵类型(几个部分专用的模板重载)……每种类型都有自己的错误处理。但是感谢您的解决方案!
  • 哦,谢谢更新。我已经包含(并测试了)您的第一个解决方案:)
【解决方案2】:

我猜(不可靠)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);
}

【讨论】:

  • 感谢提供简化的测试用例。所以,我想我们应该为此在 Eigen 或 GCC 中开一张票?
  • 一张票听起来不错。实际上可能是 2,即使错误在 gcc 中,如果可能的话,Eigen 也可能想要解决它。
  • 这在 c++17 模式下使用 g++-7.1 开始失败(但适用于 c++14)。所有 clang 版本都可以正常工作:godbolt.org/z/33PWjg 我不知道 gcc 考虑 SPT 模板的原因(可能是因为 Eigen::Matrix 有一个基础 MatrixBase&lt;Matrix&lt;...&gt; &gt;)。此外,不确定 Eigen 如何轻松解决此问题。
  • 好吧,在 GCC 的 bugzilla 中有一个人有同样的 bug:gcc.gnu.org/bugzilla/show_bug.cgi?id=84075 我会在创建我的 acc 时添加你的测试用例...
猜你喜欢
  • 2017-08-19
  • 1970-01-01
  • 1970-01-01
  • 2011-09-29
  • 2019-04-10
  • 1970-01-01
  • 2018-10-31
  • 1970-01-01
  • 2022-11-08
相关资源
最近更新 更多