2012-08-25 27 views
5

Arka plan soru: boost.proto + detect invalid terminal before building the expression tree.boost.proto + İfade ağacını yerinde değiştir

Merhaba, ne ben başarmak için çalışıyorum

  1. tüm vektörler onların başlamak yineleyiciler ile ikame edilir bir ifade ağacının, bir kopyasını oluşturmak (benim durumumda bir ham işaretçi edilir)
  2. olduğunu Yineleyicileri yinelemek için
  3. ağaçtaki dereference yineleyicileri, ancak bu bölüm nispeten kolay olmalıdır.

Yani, 1 için bu kod yukarıdaki tüm vektörler işaretçiler yerini bir ağacı oluşturmak için başarılı

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

ile sona erdi. Çok uzak çok iyi. Şimdi yineleyicileri artırmaya çalışın. Yineleyicileri ilerletmenin daha iyi olacağını fark ettim, bu yüzden sadece bir dönüşümle, rastgele bir erişim yineleyicisinin davranışının çoğunu elde edebilirim (dereference diğer eksik parçadır). 2. için, ana işlevi, Şimdi

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

olmalıdır dönüşümü gerekli

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

alıyorum (önemsiz filtrelenen) aşağıdaki hata iletisi:

array.cpp: 361: 13: hata: salt okunur yerin tahsisi

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

beni asıl rahatsız olduğu '((* & var)) 'kısım ... bunu düzeltmek için ne yapacağımı anlayamıyorum. peşin sayesinde, saygılarımla

PS İlgisiz şey: dönüşümler biraz oynadıktan sonra, ben kullanıyorum genel kalıptır:

  1. ağaca neler yapacağınıza karar verin
  2. İşlemi gerçekleştiren bir ilkel dönüşüm yazın
  3. Dönüştürmenin uygulanacağı yeri tanımlayan bir dilbilgisi yazın.

Bunun makul olduğunu düşünüyor musunuz? Yani, tek bir tür düğümüne sadece bir temel işlem gerçekleştirmek için çok fazla kod var. Bağlamlarda, düğüm tipinde ayrım yaparak, bir kerede birden fazla ops tanımlamak mümkündür. Bunu da dönüşümlerle yapmak mümkün mü? Kullanılacak genel model nedir?

+0

Hata iletisi, 'var' (indeksle arttırma girişiminde bulunduğunuz yer) immutable olduğu anlamına gelir. Dönüşümün bir sonraki yineleyiciyi döndürdüğü daha işlevsel bir stili kullanmayı denediniz mi? –

+0

@LucDanton İter_advance içinde dönüş türünü değiştirirseniz ve değiştirilmiş bir işaretçi döndürdüm (dönüştürmede işaretçinin artırıldığını doğruladım), ağaç değiştirilmez. Ben proto-manaual üzerinde 'increment_ints' takip ediyordum, ama şimdi fark ettim ki, bu durumda, ağaç int vars'a referanslar saklıyordu, simdi ptr'lerin ağaçtaki değere göre depolandım. Alternatifler: 1. Arttırdığım her seferinde bütün ağacın yeni bir kopyasını hazırlayın (tamamen işlevsel yaklaşım?) B) imleci, kılavuzun 'karışık' örneğinde olduğu gibi bir iterator_wrapper içinde saklayın. – Giuliano

cevap

4

Sezginiz doğrudur; ağacın yerinde değiştirilebilmesi gerekir. Protol'ün araştırmam gereken pass_through dönüşümü ile ilgili biraz tuhaflık var gibi görünüyor, bu yüzden çözüm biraz açık değil. İlk olarak, Proto algoritmalarında kullanacağım bazı callables'leri tanımlarım. İlkel dönüşümlere geçişleri tercih ederim çünkü bunlar daha basit, daha yeniden kullanılabilir ve daha kolay okunabilen Proto algoritmalarıyla sonuçlanır.

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

Şimdi, basit bir yineleyici adaptörü ile kırılganlık sorunu çözmek:

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

İşte vektör yineleyicinızı içeren bir ağaca vektörleri içeren bir ağaç açmak için algoritma var.

// 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> >) 
     > 
    > 
{}; 

Son proto::_byval gerekli olmamalıdır. proto::nary_expr tarafından kullanılan pass_through dönüşümü, const geçici düğümleri oluşturmamalıdır. Bunun için üzgünüm.

Ve burada tüm yineleyicileri yerinde ilerleten algoritma. Bunu tamamen düzeltebildiğin zaman, gerçekten bir proto ustası olacaksın.

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

yukarıda anlamanın püf noktası bilmektir:

  1. proto::_void hiçbir şey yapmaz ve böyle bir dönüşüm olarak kullanılan void
  2. proto::and_, döner, belirtilen tüm dönüşümleri gerçekleştirir ve sonucu döndürür sonuncusu. Bütün bunlardan sonra

, şimdi yapmanız gereken yola çıkmıştı ne yapabilirim: yineleyicinızı içeren bir ağaca vektörleri içeren bir ağaç çevirin ve sonra tüm yineleyicinızı ilerlemek yerinde:

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

Bence kod tuhaflık içine girmemiş olsaydın işe yarayacaktı. Ayrıca, ilkel dönüşümler yerine sıradan satırlar yazıyorsanız, daha kolay bir zamana sahip olabileceğinizi düşünüyorum.

Bu yardımcı olur umarım.

+0

Hey Eric, bu çözümü kontrol etmem için bana biraz zaman verin (ve kodunuzu anlayın), cevabınızı kabul etmeden önce (biraz meşgul 'sabahı') emin olun, bu konuda hiç şüpheniz yok! Ama şimdi sana şükürler olsun, şükürler olsun, benim için çok iş yaptın. BTW ben proto-bağımlılık buluyorum ... :) (PS: 1.50 artırmak kullanarak) – Giuliano

+2

FYI, 'pass_through' dönüşümü const tuhaflık destek bagaja sabittir. 1.52'nin bir parçası olmalı. Geri dönüşünüz için teşekkür ederiz; Prototan hoşlandığına sevindim! :) –

+0

Tamam, kod benim için çalışıyor, teşekkürler. BTW, aynı zamanda, gündelik görevler için “güçlendirmek” amacıyla diğer güçlendirici lib'leri (boost.range, boost.iterator ...) kullanmanın güzel bir örneğidir. İki soru: 1 Neden her iki sonucu da uzmanlaşıyorsunuz? ve sonucu callables. Bunun ağaçta E'nin kabalığıyla ilgili olduğunu görüyorum, fakat belirli bir durumda hangi uzmanlıkların gerekli olduğunu nasıl tahmin edebilirim (hiç bir teşebbüsle)? 2. Son dönüşüm: anlamadığım birkaç şey var, ama en önemlisi: niçin en içteki vector_advance_algo'yu and_ dönüşümü ile karıştırıyorsun?Tekrar teşekkürler! – Giuliano