如何围绕表达式模板编写第三方库包装类

How to write a third-party library wrapper class around expression templates

本文关键字:第三方 包装类 何围绕 表达式      更新时间:2023-10-16

我们正试图在我的研究小组中实现一个新的C++代码来执行大型数值模拟(有限元、有限差分方法、拓扑优化等)该软件将被学术界和工业界人士使用。

对于软件的密集线性代数部分,我们希望使用特征或犰狳。我们希望围绕这些包构建一个包装器,原因有两个:1. 向用户公开我们自己的 API,而不是第三方 API;和 2.以防我们将来需要切换库。我知道原因 2 是一种非常昂贵的保险形式,但我们在以前的模拟软件中遇到了这种情况。

我遇到的有关包装第三方库的信息来自以下来源:

  • 是否应该在我的C++库的 API 中公开第三方类型

  • https://softwareengineering.stackexchange.com/questions/107338/using-third-party-libraries-always-use-a-wrapper

我的问题与构建此包装器类的最佳方法有关。 理想情况下,薄层包装器是最好的,因为:

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

由于表达式模板的实现,无需创建任何临时。同样的情况也适用于本征。

据我

所知,我失去表达式模板功能的原因是,虽然犰狳或本征不会创建自己的临时,但我的类my_vec可以。避免这种情况的唯一方法是在其表达式模板周围构建一个薄层包装器。然而,在这一点上,这似乎违反了YAGNI原则。

这个相关的问题在这里:

  • 如何集成使用表达式模板的库?

建议使用类似以下内容:

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,请使用别名?

或者构建这个包装类最方便的方法是什么?

仅供将来参考,这就是我决定实现解决方案的方式: 我通过以下方式重载了运算符+:

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