2013-04-24 71 views
1

我試圖將懶惰評估引入到現有的代碼項目中。項目核心基本上包含大量使用自定義類型的計算(它的行爲類似於double,但在後臺執行額外的工作)。非侵入性地用表達式樹替換定製類型

我們的目標是引入懶惰的評估概念,使用boost proto來優化現有表達式。

限制:

  • 現有的計算不能觸及
  • 使用由一個typedef定義的,所以更換類型本身的類型可能

我們試圖實現概念的簡單證明,但沒有管理代碼的行爲如預期。這就是我們走到這一步:

#include <boost/proto/proto.hpp> 
#include <complex> 
#include <iostream> 

using namespace std; 
using namespace boost; 
using namespace boost::proto; 

// The custom implemented Type 
typedef std::complex<double> my_type; 

// The Basic expression wrapper 
template< typename Expr = proto::terminal<my_type>::type > 
struct MyDoubleExpr 
    : proto::extends< Expr, MyDoubleExpr<Expr>, proto::default_domain > 
{ 
    typedef 
     proto::extends< Expr, MyDoubleExpr<Expr>, proto::default_domain > 
    base_type; 

    MyDoubleExpr(Expr const &expr = Expr()) 
     : base_type(expr) 
    {} 

    // Overloading of all Constructors supported by the custom type 
    typedef typename proto::terminal<my_type>::type expr_type; 
    MyDoubleExpr(double const &d) 
    : base_type(expr_type::make(my_type(d))) 
    {} 

    // Lazy assignment is desired 
    BOOST_PROTO_EXTENDS_USING_ASSIGN(MyDoubleExpr) 

}; 

// unintrusively replace the existing type with 
// the expression template 
typedef MyDoubleExpr<> replaced_type; 


int main() { 

    replaced_type a = 2.0, b = 1.5; 

    proto::default_context ctx; 

    // The replaced type is created as template specialisation 
    // proto::terminal<my_type>::type -> cannot store expressions 
    replaced_type c; 
    c = (a + b) * 2.0; 
    std::cout << "c: " << proto::eval(c,ctx) << endl << endl; 
    proto::display_expr(c); 

    // Initialisation does not work directly ? 
    //replaced_type d = a+b; 

    // using auto works fine, so the expression basically works 
    auto e = (a + b) * 2.0; 
    std::cout << "e: " << proto::eval(e,ctx) << endl; 
    proto::display_expr(e); 

    getchar(); 
    return 0; 
} 

我們的主要問題是,我們不能定義一個類型,既文字和表情一樣的作品。在這個例子中,c是proto :: terminal類型的表達式,並且忽略表達式的賦值。使用自動存儲表達式時,它工作正常。而且,直接初始化是不可能的。

如果我正確理解我們的問題,我們需要兩種不同類型的表達式和文字,這是不可能的,因爲我們只能改變現有的類型。

我們還研究了其他選項,例如使用BOOST_PROTO_DEFINE_OPERATORS(...)使我們的自定義類型成爲非侵入性終端,但懶惰賦值也是不可能的。

所以,我們的問題是如果我們能夠實現我們想要的或者我們必須改變現有的代碼來引入懶惰評估?

感謝您的幫助, 馬蒂亞斯enter code here

回答

1

看一看的lazy vector example從原的文檔。惰性向量表達式有一個lazy_vector_expr模板,以及專門用於終端的lazy_vector模板(它繼承了lazy_vector_expr的大部分實現)。 lazy_vector定義了一個+=運算符,該運算符接受任意延遲向量表達式,對其進行求值並存儲結果。

這聽起來像你在做類似的事情。但是,而不是+=,你正在尋找普通的任務和轉換建設。因此定義一個模板的構造函數和賦值運算符。喜歡的東西(未經測試)...

template<typename XXX = proto::is_proto_expr> // hack, needed to for ADL 
struct replaced_type_t 
    : MyDoubleExpr< proto::terminal<double>::type > 
{ 
    replaced_type_t(double d = 0.0) 
     : MyDoubleExpr< proto::terminal<double>::type >(
      proto::terminal<double>::type::make(d) 
     ) 
    {} 
    // Converting constructor 
    template<typename OtherExpr> 
    replaced_type_t(MyDoubleExpr<OtherExpr> const & e) 
    { 
     // Evaluate e, assign to *this 
    } 
    // Converting assignment 
    template<typename OtherExpr> 
    replaced_type_t& operator=(MyDoubleExpr<OtherExpr> const & e) 
    { 
     // Evaluate e, assign to *this 
    } 
}; 

typedef replaced_type_t<> replaced_type; 

順便說一句,當你使用BOOST_PROTO_EXTENDS_USING_ASSIGN,你得到了什麼是建立一個更大的表達重載賦值運算符。所以這個:

replaced_type c; 
c = (a + b) * 2.0; 

...將不得不創建一個臨時MyDoubleExpr<>含分配節點,然後把它扔的平平無奇的效果。

HTH!

+0

謝謝。得到它與輕微的變化(我不得不直接使用OtherExpr而不是MyDoubleExpr 作爲賦值參數)。評估和計算工作正常。唯一的問題是這會急於計算每一個表達式。爲了獲得更好的優化選項,如果賦值不評估表達式,而是懶惰地存儲它,那將會很好。 – MatthiasB 2013-04-26 16:37:27

+0

不確定你的意思是「懶惰地存儲」。你想什麼時候評估它? – 2013-04-26 18:43:19

+0

最好的情況下,只有當我在程序結束時調用proto :: eval(c,ctx)時。在此之前,任何表達式只能存儲。我們理想的用例是我們可以在整個程序(包含一組輸入參數和一個結果值)上構建一個表達式樹,跨越幾個計算和函數調用。 – MatthiasB 2013-04-26 20:20:46