Записать произвольный собственный объект в обычное хранилище строк - PullRequest
0 голосов
/ 21 февраля 2019

Я пишу модуль для записи данных в файл, который по соглашению использует только основное хранилище строк.Я бы хотел, чтобы моя функция могла разрешать входные объекты как основных, так и основных столбцов.

В настоящее время я сначала использую Eigen, чтобы скопировать основной объект столбца в основной объект, прежде чемнаписать.Мой код работает хорошо в большинстве случаев, но для Eigen::VectorXi компиляция завершается неудачно с утверждением, которое я не понимаю. Как мне решить эту проблему? Можно ли избежать создания многих дел?

Код (запись имитируется выводом std::vector):

#include <vector>
#include <iostream>
#include <Eigen/Eigen>

template <class T, int Rows, int Cols, int Options, int MaxRows, int MaxCols>
std::vector<T> write(const Eigen::Matrix<T,Rows,Cols,Options,MaxRows,MaxCols>& matrix)
{
    std::vector<T> data(static_cast<size_t>(matrix.size()));

    if (matrix.IsRowMajor) {
        std::copy(matrix.data(), matrix.data()+matrix.size(), data.begin());
        return data;
    } else {
        Eigen::Matrix<T, Rows, Cols, Eigen::RowMajor, MaxRows, MaxCols> tmp = matrix;
        return write(tmp);
    }
}

int main()
{
    Eigen::VectorXi matrix = Eigen::VectorXi::LinSpaced(10, 0, 9);

    std::vector<int> output = write(matrix);
}

Ошибка компиляции:

In file included from test.cpp:3:
In file included from /usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/Eigen:1:
In file included from /usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/Dense:1:
In file included from /usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/Core:457:
/usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/src/Core/PlainObjectBase.h:903:7: error: static_assert failed "INVALID_MATRIX_TEMPLATE_PARAMETERS"
      EIGEN_STATIC_ASSERT((EIGEN_IMPLIES(MaxRowsAtCompileTime==1 && MaxColsAtCompileTime!=1, (Options&RowMajor)==RowMajor)
      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/src/Core/util/StaticAssert.h:33:40: note: expanded from macro 'EIGEN_STATIC_ASSERT'
    #define EIGEN_STATIC_ASSERT(X,MSG) static_assert(X,#MSG);
                                       ^             ~
/usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/src/Core/PlainObjectBase.h:535:7: note: in instantiation of member function 'Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 1, -1, 1>
      >::_check_template_params' requested here
      _check_template_params();
      ^
/usr/local/Cellar/eigen/3.3.7/include/eigen3/Eigen/src/Core/Matrix.h:377:9: note: in instantiation of function template specialization 'Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 1, -1, 1>
      >::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >' requested here
      : Base(other.derived())
        ^
test.cpp:14:79: note: in instantiation of function template specialization 'Eigen::Matrix<int, -1, 1, 1, -1, 1>::Matrix<Eigen::Matrix<int, -1, 1, 0, -1, 1> >' requested here
        Eigen::Matrix<T, Rows, Cols, Eigen::RowMajor, MaxRows, MaxCols> tmp = matrix;
                                                                              ^
test.cpp:23:31: note: in instantiation of function template specialization 'write<int, -1, 1, 0, -1, 1>' requested here
    std::vector<int> output = write(matrix);
                              ^
1 error generated.

Ответы [ 2 ]

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

Более простое решение состоит в том, чтобы Eigen::Ref выполнил всю работу за вас:

Ref<const Matrix<T,Rows,Cols,Cols==1?ColMajor:RowMajor,MaxRows,MaxCols>,0, InnerStride<1> > row_maj(matrix);

Тогда гарантированно будет последовательно сохраняться row_maj в главном порядке строк.Если matrix совместимо, то копирование не происходит.Нет веток, нет SFINAE и т. Д.

Здесь matrix может быть любым выражением, не только Matrix<...>, но также подматрицами, Map, другим Ref и т. Д.

Чтобы обработать любые выражения, просто замените Rows и т.п. на XprType::RowsAtCompileTime, где XprType - это тип matrix.

template <class XprType>
std::vector<typename XprType::Scalar> write(const Eigen::MatrixBase<XprType>& matrix)
{...}
0 голосов
/ 21 февраля 2019

Понимание статического утверждения

К сожалению, утверждение на самом деле не говорит само за себя, и единственное, что вы можете из него получить, это намек, что что-то не так с параметрами вашего шаблона.Если мы посмотрим на исходный код Эйгена Исходный код Эйгена , мы найдем следующее начало в строке 903:

EIGEN_STATIC_ASSERT((EIGEN_IMPLIES(MaxRowsAtCompileTime==1 && MaxColsAtCompileTime!=1, (Options&RowMajor)==RowMajor)
                        && EIGEN_IMPLIES(MaxColsAtCompileTime==1 && MaxRowsAtCompileTime!=1, (Options&RowMajor)==0)
                        && ((RowsAtCompileTime == Dynamic) || (RowsAtCompileTime >= 0))
                        && ((ColsAtCompileTime == Dynamic) || (ColsAtCompileTime >= 0))
                        && ((MaxRowsAtCompileTime == Dynamic) || (MaxRowsAtCompileTime >= 0))
                        && ((MaxColsAtCompileTime == Dynamic) || (MaxColsAtCompileTime >= 0))
                        && (MaxRowsAtCompileTime == RowsAtCompileTime || RowsAtCompileTime==Dynamic)
                        && (MaxColsAtCompileTime == ColsAtCompileTime || ColsAtCompileTime==Dynamic)
                        && (Options & (DontAlign|RowMajor)) == Options),
        INVALID_MATRIX_TEMPLATE_PARAMETERS)

Даже если компилятор указывает, что

EIGEN_IMPLIES(MaxRowsAtCompileTime==1 && MaxColsAtCompileTime!=1, (Options&RowMajor)==RowMajor)

вызываетошибка, действительно следующая строка:

EIGEN_IMPLIES(MaxColsAtCompileTime==1 && MaxRowsAtCompileTime!=1, (Options&RowMajor)==0)

Понимание того, что вызывает утверждение

Вы предоставляете Eigen::VectorXi в качестве ввода для write.Eigen::VectorXi на самом деле просто typedef для

Eigen::Matrix<int, Eigen::Dynamic, 1, Eigen::ColMajor, Eigen::Dynamic, 1>

Поэтому строка

Eigen::Matrix<T, Rows, Cols, Eigen::RowMajor, MaxRows, MaxCols> tmp = matrix;

в write расширяется до

Eigen::Matrix<int, Eigen::Dynamic, 1, Eigen::RowMajor, Eigen::Dynamic, 1> tmp = matrix;

, что вызывает утверждение,поскольку матрица с MaxColsAtCompileTime==1 и MaxRowsAtCompileTime!=1 не должна быть RowMajor.

Решите вашу проблему

Проблема в том, что даже если вы можете проверить,ваша входная матрица является векторной, мажорной или столбцовой, вы не можете объявить

Eigen::Matrix<T, Rows, Cols, Eigen::RowMajor, MaxRows, MaxCols>

, если это не разрешено делать во время компиляции (и это не из-за статического утверждения).

У вас есть следующие опции, чтобы ваш код работал:

1.if constexpr (C ++ 17)

C ++ 17 предлагает способ определения во время компиляции, будет ли определенная условная ветвь взята или нет.Недостатком этого подхода (помимо требования к компилятору C ++ 17) является то, что вы можете проверять только константные выражения.

В конкретном примере это выглядит так:

template <class T, int Rows, int Cols, int Options, int MaxRows, int MaxCols>
std::vector<T> write(const Eigen::Matrix<T, Rows, Cols, Options, MaxRows, MaxCols>& matrix)
{
  typedef Eigen::Matrix<T, Rows, Cols, Options, MaxRows, MaxCols> MatrixType;
  std::vector<T> data(static_cast<size_t>(matrix.size()));

  if constexpr (MatrixType::MaxRowsAtCompileTime == 1 || 
                MatrixType::MaxColsAtCompileTime ==1 ||
               (MatrixType::Options&Eigen::RowMajor) == Eigen::RowMajor) {
    std::copy(matrix.data(), matrix.data() + matrix.size(), data.begin());
    return data;
  } else {
    Eigen::Matrix<T, Rows, Cols, Eigen::RowMajor, MaxRows, MaxCols> tmp = matrix;
    return write(tmp);
  }
}

2.SFINAE

Вы можете отправить вызов на write во время компиляции, используя SFINAE, используя std::enable_if.В следующем примере используется слегка измененная версия исходного кода, но все должно быть ясно из контекста:

// matrix is either a vector or in row-major
template <typename Derived>
std::vector<typename Derived::Scalar> write(const Eigen::MatrixBase<Derived>& matrix,
  typename std::enable_if<Derived::MaxRowsAtCompileTime == 1 ||
                          Derived::MaxColsAtCompileTime == 1 ||
                          (Derived::Options & Eigen::RowMajor) == Eigen::RowMajor,
                          Derived>::type* = 0)
{
  std::vector<typename Derived::Scalar> data(
      static_cast<size_t>(matrix.size()));
  std::copy(matrix.derived().data(), matrix.derived().data() + matrix.size(),
            data.begin());
  return data;
}

// matrix is neither a vector nor in row-major
template <typename Derived>
std::vector<typename Derived::Scalar> write(const Eigen::MatrixBase<Derived>& matrix,
  typename std::enable_if<Derived::MaxRowsAtCompileTime != 1 &&
                          Derived::MaxColsAtCompileTime != 1 &&
                          (Derived::Options & Eigen::RowMajor) == 0,
                          Derived>::type* = 0)
{
  Eigen::Matrix<typename Derived::Scalar, Derived::RowsAtCompileTime,
                Derived::ColsAtCompileTime, Eigen::RowMajor,
                Derived::MaxRowsAtCompileTime, Derived::MaxColsAtCompileTime> tmp = matrix;
  return write(tmp);
}

Это работает с использованием компилятора C ++ 11.

Другие варианты:специализируйте шаблон, но он станет еще длиннее, чем подход SFINAE.

Некоторые тестовые случаи:

Eigen::Matrix<int, 3, 3, Eigen::RowMajor> m;
m << 1, 2, 3, 
     1, 2, 3,
     1, 2, 3;

std::vector<int> output = write(m);

for (const auto& element : output) {
  std::cout << element << " ";
}

Вывод: 1 2 3 1 2 3 1 2 3

Eigen::Matrix<int, 3, 3, Eigen::ColMajor> m;
m << 1, 2, 3, 
     1, 2, 3,
     1, 2, 3;

std::vector<int> output = write(m);

for (const auto& element : output) {
  std::cout << element << " ";
}

Выход: 1 2 3 1 2 3 1 2 3

Eigen::VectorXi m = Eigen::VectorXi::LinSpaced(10, 0, 9);

std::vector<int> output = write(m);

for (const auto& element : output) {
  std::cout << element << " ";
}

Выход: 0 1 2 3 4 5 6 7 89

Eigen::RowVectorXi m = Eigen::RowVectorXi::LinSpaced(10, 0, 9);

std::vector<int> output = write(m);

for (const auto& element : output) {
  std::cout << element << " ";
}

Выход: 0 1 2 3 4 5 6 7 8 9

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...