Как написать сторонний библиотечный класс-оболочку вокруг шаблонов выражений
Мы пытаемся внедрить новый код 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;
}