Eigen Matrix + Boost :: Сериализация / C ++ 17 - PullRequest
0 голосов
/ 05 февраля 2019

Я пытаюсь включить C ++ 17 для нашей кодовой базы, основанной на boost - и boost :: serialization для промежуточного хранения данных и сериализации перед передачей.

В целом все выглядит хорошои, кажется, работает, за исключением случаев, когда мы сериализуем объекты Eigen :: Matrix и , включая заголовок поддержки расширенной сериализации для совместной сериализации ptr.

Минимальный пример / тестовый код на github: https://github.com/nightsparc/EigenSerialize

[EDIT] @Marc Glisse предоставил уменьшенный тестовый пример ниже.См .: https://stackoverflow.com/a/54536756/1267320

Я провел несколько тестов с разными компиляторами (GCC6 / 7/8 и Clang6).Мы обычно используем систему GCC, которая является GCC7.3 для Ubuntu 18.04.Для меня это, кажется, проблема, связанная с режимом C ++ 17 GCC7 и выше.

Я имею в виду, я не использую shared_ptr в минимальном примере, поэтому я могу удалить его, и все будет хорошо ... тем не менее, в нашей кодовой базе shared_ptrs везде сериализуются.

У кого-нибудь из вас есть идеи, что здесь происходит?Или это ошибка в режиме 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]
Повышение PR: https://github.com/boostorg/serialization/pull/144

Ответы [ 2 ]

0 голосов
/ 05 февраля 2019

Я бы предположил (ненадежно), что разница между C ++ 14 и C ++ 17 обусловлена ​​вычетом аргументов шаблона (компилятор не может отклонить шаблон только из-за слишком малого числа параметров), что делает gccне справиться как сфина.Я не знаю, есть ли ошибка в 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);
}
0 голосов
/ 05 февраля 2019

Не уверен насчет ошибки, но зачем вам нужен 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 или версии Eigen-default для clang 5/6 и gcc 6/7/8.

Я добавил 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};};
} } 
...