c++14 - 具有 unique_ptr 和矩阵的持久表达式模板

标签 c++14 eigen continuation-passing expression-templates

我想使用表达式模板来创建在语句中持续存在的对象树。构建树最初涉及使用特征线性代数库进行一些计算。持久表达式模板将具有额外的方法来通过以不同方式遍历树来计算其他数量(但我还没有做到)。

为了避免临时变量超出范围的问题,子表达式对象通过 std::unique_ptr 进行管理。当构建表达式树时,指针应该向上传播,以便保 stub 对象的指针确保所有对象保持事件状态。由于 Eigen 创建的表达式模板包含对语句末尾超出范围的临时变量的引用,因此情况变得复杂,因此在构造树时必须评估所有 Eigen 表达式。

下面是一个按比例缩小的实现,当 val 类型是保存整数的对象时,它似乎可以工作,但对于 Matrix 类型,它在构造 output_xpr 时会崩溃目的。崩溃的原因似乎是 Eigen 的矩阵乘积表达式模板 (Eigen::GeneralProduct) 在使用之前已损坏。但是,在崩溃发生之前,我自己的表达式对象或 GeneralProduct 的析构函数似乎都没有被调用,并且 valgrind 没有检测到任何无效的内存访问。

任何帮助将不胜感激!我也很感谢有关我将移动构造函数与静态继承一起使用的评论,也许问题出在某个地方。

#include <iostream>
#include <memory>

#include <Eigen/Core>

typedef Eigen::MatrixXi val;

// expression_ptr and derived_ptr: contain unique pointers
// to the actual expression objects

template<class Derived>
struct expression_ptr {
    Derived &&transfer_cast() && {
        return std::move(static_cast<Derived &&>(*this));
    }
};

template<class A>
struct derived_ptr : public expression_ptr<derived_ptr<A>> {
    derived_ptr(std::unique_ptr<A> &&p) : ptr_(std::move(p)) {}
    derived_ptr(derived_ptr<A> &&o) : ptr_(std::move(o.ptr_)) {}

    auto operator()() const {
        return (*ptr_)();
    }

private:
    std::unique_ptr<A> ptr_;
};

// value_xpr, product_xpr and output_xpr: expression templates
// doing the actual work

template<class A>
struct value_xpr {
    value_xpr(const A &v) : value_(v) {}

    const A &operator()() const {
        return value_;
    }

private:
    const A &value_;
};

template<class A,class B>
struct product_xpr {
    product_xpr(expression_ptr<derived_ptr<A>> &&a, expression_ptr<derived_ptr<B>> &&b) :
        a_(std::move(a).transfer_cast()), b_(std::move(b).transfer_cast()) {
    }

    auto operator()() const {
        return a_() * b_();
    }

private:
    derived_ptr<A> a_;
    derived_ptr<B> b_;
};

// Top-level expression with a matrix to hold the completely
// evaluated output of the Eigen calculations
template<class A>
struct output_xpr {
    output_xpr(expression_ptr<derived_ptr<A>> &&a) :
        a_(std::move(a).transfer_cast()), result_(a_()) {}

    const val &operator()() const {
        return result_;
    }

private:
    derived_ptr<A> a_;
    val result_;
};

// helper functions to create the expressions

template<class A>
derived_ptr<value_xpr<A>> input(const A &a) {
    return derived_ptr<value_xpr<A>>(std::make_unique<value_xpr<A>>(a));
}

template<class A,class B>
derived_ptr<product_xpr<A,B>> operator*(expression_ptr<derived_ptr<A>> &&a, expression_ptr<derived_ptr<B>> &&b) {
    return derived_ptr<product_xpr<A,B>>(std::make_unique<product_xpr<A,B>>(std::move(a).transfer_cast(), std::move(b).transfer_cast()));
}

template<class A>
derived_ptr<output_xpr<A>> eval(expression_ptr<derived_ptr<A>> &&a) {
    return derived_ptr<output_xpr<A>>(std::make_unique<output_xpr<A>>(std::move(a).transfer_cast()));
}

int main() {
    Eigen::MatrixXi mat(2, 2);
    mat << 1, 1, 0, 1;
    val one(mat), two(mat);
    auto xpr = eval(input(one) * input(two));
    std::cout << xpr() << std::endl;
    return 0;
}

最佳答案

您的问题似乎是您正在使用其他人的表达式模板,并将结果存储在 auto 中.

(这发生在 product_xpr<A>::operator() 中,您在其中调用 * ,如果我没看错的话,它是使用表达式模板的特征乘法)。

表达式模板通常被设计为假设整个表达式将出现在一行上,并且将以导致表达式模板被求值的接收器类型(如矩阵)结束。

就您而言,您有 a*b表达式模板,然后使用它来构造表达式模板返回值,稍后您将对其求值。临时对象的生命周期传递给 *a*b当您到达接收器类型(矩阵)时,将会结束,这违反了表达式模板的预期。

我正在努力想出一个解决方案来确保所有临时对象的生命周期都得到延长。我的一个想法是某种连续传递风格,而不是调用:

Matrix m = (a*b);

你愿意

auto x = { do (a*b) pass that to (cast to matrix) }

替换

auto operator()() const {
    return a_() * b_();
}

template<class F>
auto operator()(F&& f) const {
    return std::forward<F>(f)(a_() * b_());
}

其中“下一步”被传递给每个子表达式。对于二进制表达式来说,这会变得更加棘手,因为您必须确保第一个表达式的计算调用导致第二个子表达式被计算的代码,并且然后将两个表达式组合起来,全部在同一个长递归调用堆栈中。

我对连续传递风格还不够精通,无法完全解开这个结,但它在函数式编程世界中有点流行。

另一种方法是将树展平为可选元组,然后使用奇特的operator()构造树中的每个可选,并以这种方式手动连接参数。基本上对中间值进行手动内存管理。如果特征表达式模板是移动感知的或者没有任何自指针,那么这将起作用,这样在构造点移动就不会破坏东西。写下来会很有挑战性。

关于c++14 - 具有 unique_ptr 和矩阵的持久表达式模板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29677129/

相关文章:

c# - 连续传递风格的中间值和返回值

c++ - 标准说在发现未知属性时实现应该做什么?

c++ - Python Numpy方法对应C++ Eigen make crash

c++ - 如何在 Eigen 库中获取矩阵的秩?

c++ - 来自函数的高效特征矩阵

javascript - memoize continuation passing style 函数

haskell - 是否可以将 HOAS 函数转换为连续传递样式?

c++ - 保留一个 reference_wrapper 对象的 vector ,这怎么可能?

c++ - 使用 boost::optional 捕获和包装异常

c++ - 如何从构造函数参数初始化模板成员数组?