2012-08-25 67 views
5

背景問題:boost.proto + detect invalid terminal before building the expression treeboost.proto +修改表達式樹

嗨,我想要做到的,是

  1. 創建一個表達式樹,所有的向量取代有 他們開始迭代器的副本(在我的情況是一個原始指針)
  2. 增加迭代器到樹中,但該部分應該相對容易。

所以,對於1.我結束了這段代碼

/////////////////////////////////////////////////////////////////////////////// 
// A transform that converts all vectors nodes in a tree to iterator nodes 
struct vector_begin : proto::transform <vector_begin> 
{ 
    template<typename Expr, typename Unused1, typename Unused2> 
    struct impl : boost::proto::transform_impl<Expr, Unused1, Unused2> 
    { 
     // must strip away the reference qualifier (&) 
     typedef typename proto::result_of::value< 
       typename boost::remove_reference<Expr>::type 
      >::type vector_type; 

     typedef typename proto::result_of::as_expr 
      <typename vector_type::const_iterator>::type result_type; 

     result_type operator()(
       typename impl::expr_param var 
      , typename impl::state_param 
      , typename impl::data_param) const 
     { 
      typename vector_type::const_iterator iter(proto::value(var).begin()); 
      return proto::as_expr(iter); // store iterator by value 
     } 
    }; 
}; 

struct vector_grammar_begin 
     : proto::or_ < 
      proto::when <vector_terminal, vector_begin> 
      // scalars want to be stored by value (proto stores them by const &), if not the code does not compile... 
      , proto::when <scalar_terminal, boost::proto::_make_terminal(boost::proto::_byval(boost::proto::_value))> 
      // descend the tree converting vectors to begin() iterators 
      , proto::when <proto::nary_expr<_, proto::vararg<vector_grammar_begin> > > 
     > 
{}; 

以上成功創建所有矢量由指針取代了樹。到現在爲止還挺好。現在,嘗試增加 迭代器。我意識到提升迭代器會更好,所以只需一次轉換,我就可以獲得隨機訪問迭代器的大部分行爲(取消引用是另一個缺失的部分)。對於2,所需的轉換應該是

/////////////////////////////////////////////////////////////////////////////// 
// A transform that advances all iterators in a tree 
struct iter_advance : proto::transform <iter_advance> 
{ 
    template<typename Expr, typename Index, typename Dummy> 
    struct impl : boost::proto::transform_impl<Expr, Index, Dummy> 
    { 
     typedef void result_type; 
     result_type operator()(
       typename impl::expr_param var 
      , typename impl::state_param index // i'm using state to pass a data :(
      , typename impl::data_param) const 
     { 
      proto::value(var)+=index; // No good... compile error here :(
     } 
    }; 
}; 

// Ok, this is brittle, what if I decide the change vector<D,T>'s iterator type ? 
struct iter_terminal 
     : proto::and_< 
       proto::terminal<_> 
      , proto::if_<boost::is_pointer<proto::_value>()> 
      > 
{}; 


struct vector_grammar_advance 
     : proto::or_ < 
      proto::when <iter_terminal, iter_advance> 
      , proto::terminal<_> 
      , proto::when <proto::nary_expr<_, proto::vararg<vector_grammar_advance> > > 
     > 
{}; 

現在,在主要功能

template <class Expr> 
void check_advance (Expr const &e) 
{ 
    proto::display_expr (e); 

    typedef typename boost::result_of<vector_grammar_begin(Expr)>::type iterator_type; 
    iterator_type iter = vector_grammar_begin()(e); 
    proto::display_expr (iter); 

    vector_grammar_advance()(iter,1); 
    proto::display_expr (iter); 
} 

int main (int, char**) 
{ 
    vec<3, double> a(1), b(2), c(3); 
    check_advance(2*a+b/c); 
    return 0; 
} 

我收到以下錯誤消息(過濾掉垃圾):

array.cpp: 361:13:錯誤:只讀位置的分配

'boost::proto::value<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, 
boost::proto::argsns_::term<const double*>, 0l> >((* & var))' 

讓我困擾的是「((* & var))'部分...不知道該怎麼做才能解決這個問題。 在此先感謝,最好的問候

PS 無關的事:打一點與轉換之後,我使用的一般模式是:

  1. 決定做什麼樹
  2. 編寫一個執行操作的基本轉換
  3. 編寫一個語法,用於識別應該應用轉換的位置,使用之前定義的轉換

你認爲這是否合理?我的意思是,執行一個簡單的節點類型的代碼很多。通過上下文,可以一次定義多個操作,區分節點類型。 也可以用變換來做到這一點?什麼是一般的使用模式?

+0

錯誤消息意味着'var'(你試圖通過'index'增加它)是不可變的。您是否嘗試過使用更實用的樣式,其中的變換會返回下一個迭代器? –

+0

@LucDanton嘗試了,如果我在iter_advance中更改返回類型並返回一個修改過的指針(我已驗證指針在變換中增加了),樹不會更改。我正在關注proto的manual上的'increment_ints',但是現在我意識到它不同,在這種情況下,樹存儲對int變量的引用,現在我已經將ptrs按值存儲在樹中。替代方案:1.每增加一次,我就增加整個樹的新副本(純功能方法?)b)將指針存儲在iterator_wrapper中,就像手冊的「混合」示例一樣。 – Giuliano

回答

4

你的直覺是正確的;你應該能夠就地改變樹。我需要調查Proto的pass_through變換似乎有一些常見的怪異現象,所以解決方案有點不明顯。首先,我定義了一些我將在Proto算法中使用的可調參數。我更喜歡可調用的元素來進行原始轉換,因爲它們更簡單,更易重用,並且導致易於閱讀的Proto算法。

struct begin 
    : proto::callable 
{ 
    template<typename Sig> 
    struct result; 

    template<typename This, typename Rng> 
    struct result<This(Rng)> 
     : boost::range_iterator<Rng> 
    {}; 

    template<typename This, typename Rng> 
    struct result<This(Rng &)> 
     : boost::range_iterator<Rng> 
    {}; 

    template<typename Rng> 
    typename boost::range_iterator<Rng>::type 
    operator()(Rng &rng) const 
    { 
     return boost::begin(rng); 
    } 

    template<typename Rng> 
    typename boost::range_iterator<Rng const>::type 
    operator()(Rng const &rng) const 
    { 
     return boost::begin(rng); 
    } 
}; 

struct advance 
    : proto::callable 
{ 
    typedef void result_type; 

    template<typename Iter> 
    void operator()(Iter &it, unsigned d) const 
    { 
     it += d; 
    } 
}; 

現在,我解決您的脆性問題,一個簡單的迭代器適配器:

template<typename Iter> 
struct vector_iterator 
    : boost::iterator_adaptor<vector_iterator<Iter>, Iter> 
{ 
    vector_iterator() 
     : boost::iterator_adaptor<vector_iterator<Iter>, Iter>() 
    {} 

    explicit vector_iterator(Iter iter) 
     : boost::iterator_adaptor<vector_iterator<Iter>, Iter>(iter) 
    {} 

    friend std::ostream &operator<<(std::ostream &sout, vector_iterator it) 
    { 
     return sout << "vector_iterator(value: " << *it << ")"; 
    } 
}; 

這裏是把含有載體導入包含載體迭代器樹一樹的算法。

// Turn all vector terminals into vector iterator terminals 
struct vector_begin_algo 
    : proto::or_< 
     proto::when< 
      proto::terminal<std::vector<_, _> > 
      , proto::_make_terminal(
       vector_iterator<begin(proto::_value)>(begin(proto::_value)) 
      ) 
     > 
     , proto::when< 
      proto::terminal<_> 
      , proto::_make_terminal(proto::_byval(proto::_value)) 
     > 
     , proto::otherwise< 
      proto::_byval(proto::nary_expr<_, proto::vararg<vector_begin_algo> >) 
     > 
    > 
{}; 

應該不需要最後的proto::_byvalproto::nary_expr使用的pass_through轉換不應該創建const臨時節點。對於那個很抱歉。

這裏是推進所有迭代器的算法。當你能夠充分理解這一點時,你將成爲一名原始大師。

// Mutate in-place by advancing all vector iterators the amount 
// in the state parameter 
struct vector_advance_algo 
    : proto::or_< 
     proto::when< 
      proto::terminal<vector_iterator<_> > 
      , advance(proto::_value, proto::_state) 
     > 
     , proto::when< 
      proto::terminal<_> 
      , proto::_void 
     > 
     , proto::otherwise< 
      proto::and_< 
       proto::fold< 
        _ 
        , proto::_state 
        , proto::and_< 
         vector_advance_algo 
         , proto::_state 
        > 
       > 
       , proto::_void 
      > 
     > 
    > 
{}; 

訣竅理解上面的是知道:

  1. proto::_void不執行任何操作,並返回void
  2. proto::and_,作爲用於變換這樣的情況下,執行所有的指定的變換,並返回結果最後一個。

畢竟,你現在可以做您設置了什麼做出來的:將含載體到含有迭代器一樹一樹,然後推進所有的迭代器就地:

proto::literal<std::vector<int> > vec1; 
proto::value(vec1).assign(
    boost::make_counting_iterator(0) 
    , boost::make_counting_iterator(16) 
); 

auto beg = vector_begin_algo()(2 * vec1 + vec1); 
proto::display_expr(beg); 

vector_advance_algo()(beg, 1u); 
proto::display_expr(beg); 

vector_advance_algo()(beg, 1u); 
proto::display_expr(beg); 

我認爲如果你沒有碰到const奇怪的問題,你的代碼就可以工作。另外,如果您編寫普通的可調參數而不是原始轉換,我認爲您可能會有更輕鬆的時間。

希望這會有所幫助。

+0

嗨,Eric,在我接受你的答案之前,給我一些時間來檢查這個解決方案(並且理解你的代碼)(這有點忙不過來了)......這是正確的,毫無疑問!但是,現在讓我感謝你的好意,你爲我做了很多工作。順便說一句,我發現原子上癮...... :)(PS:我使用boost 1.50) – Giuliano

+2

僅供參考,'pass_through'變換中的常量奇怪在boost主幹中是固定的。它應該是1.52的一部分。感謝您的反饋;我很高興你喜歡proto! :) –

+0

好的,你的代碼適合我,謝謝。順便說一句,這也是使用其他boost庫(boost.range,boost.iterator ...)「動態地」來提升日常任務的一個很好的例子。兩個問題:1爲什麼你將專門化的結果和結果放在可調參數中。我認爲它與樹中E的常量有關,但是如何預測給定情況下需要哪些特化(不按嘗試)? 2.最終變換:有幾件事我不明白,但最重要的是:爲什麼要將最內層的vector_advance_algo包裝到and_變換中?再次感謝! – Giuliano