Как написать сторонний библиотечный класс-оболочку вокруг шаблонов выражений

Мы пытаемся внедрить новый код C++ в моей исследовательской группе для выполнения большого численного моделирования (конечные элементы, методы конечных разностей, оптимизация топологии и т. Д.). Программное обеспечение будет использоваться людьми из академических и промышленных кругов.

Для части программного обеспечения с плотной линейной алгеброй мы хотим использовать Eigen или Armadillo. Мы хотим создать оболочку вокруг этих пакетов по двум причинам: 1. предоставить пользователям собственный API, а не сторонний API; и 2. на случай, если нам понадобится переключать библиотеки в будущем. Я понимаю, что причина 2 - очень дорогая форма страхования, но мы столкнулись с этой ситуацией с нашим предыдущим программным обеспечением для моделирования.

Информация, с которой я столкнулся относительно упаковки сторонних библиотек, взята из следующих источников:

Мой вопрос касается лучшего способа создания этого класса-обёртки. В идеале лучше всего подходит тонкослойная обертка, так как:

template< typename T >
class my_vec {
private:
    arma::Col< T > _arma_vec;
};

или его эквивалент с собственным вектором.

Тогда мой класс будет вызывать сторонний библиотечный класс как:

my_vec::foo() { return _arma_vec.foo(); }

Я думаю (и мне хотелось бы получить подтверждение), что проблема с этим тонким слоем заключается в том, что я теряю скорость, получаемую от шаблонов выражений, которые эти библиотеки реализовали под капотом. Например, в броненосце следующая операция:

// Assuming these vectors were already populated.
a =  b + c + d;

становится примерно так:

for ( std::size_t i = 0; i < a.size(); ++i ) {
    a[i] = b[i] + c[i] + d[i];
}

без создания каких-либо временных из-за их реализации шаблонов выражений. Та же ситуация относится и к Эйгену.

Насколько я понимаю, причина, по которой я теряю силу шаблонов выражений, заключается в том, что хотя Armadillo или Eigen не создают свои собственные временные фильтры, мой класс my_vec делает это. Единственный способ обойти это - создать тонкую слойную оболочку вокруг их шаблонов выражений. Однако на данный момент это, по-видимому, является нарушением принципа ЯГНИ.

Этот связанный вопрос здесь:

предлагает использовать что-то вроде:

my_vec a, b, c;
// ... populate vectors
a._arma_vec = b._arma_vec + c._arma_vec;

Возможно ли использовать что-то подобное вместо этого?

template< typename T >
arma::Col< T > &
my_vec< T >::data() { return _arma_vec; }

a.data() = b.data() + c.data();

Или использовать перегрузку некоторых операторов, чтобы скрыть data() от пользователя? Какие есть альтернативы, если мы не хотим использовать библиотеки напрямую? Используете макросы? Использование псевдонимов, если мы решили использовать C++11?

Или какой самый удобный способ создать этот класс-обертку?

1 ответ

Решение

Просто для дальнейшего использования, вот как я решил реализовать свое решение: я перегрузил оператор + следующим образом:

template< typename T1, typename T2 >
auto
operator+(
        const my_vec< T1 > & X,
        const my_vec< T2 > & Y ) ->decltype( X.data() + Y.data() )
{
    return X.data() + Y.data();
}

template< typename T1, typename T2 >
auto
operator+(
        const my_vec< T1 > & X,
        const T2 &           Y ) ->decltype( X.data() + Y )
{
    return X.data() + Y;
}

template< typename T1, typename T2 >
auto
operator+(
        const T1 &           X,
        const my_vec< T2 > & Y ) ->decltype( X + Y.data() )
{
    return X + Y.data();
}

Затем я перегрузил свой оператор = в классе my_vec следующим образом:

template< typename T >
template< typename A >
const my_vec< T > &
my_vec< T >::operator=(
        const A & X )
{
    _arma_vec = X;

    return *this;
}
Другие вопросы по тегам