2010-02-25 37 views
6

Temelde benim yaptığım şu:Yineleyici yönünde nasıl parametrelendirilir?

std::set<int> indices; 
// ..fill indices 

if (flag) 
{ 
    // we need to process in ascending order 
    BOOST_FOREACH (int i, indices) 
    { 
     process(i); 
    } 
} 
else 
{ 
    // we need to process in descending order 
    BOOST_REVERSE_FOREACH (int i, indices) 
    { 
     process(i); 
    } 
} 

ben süreci (i) C++ 03 sadece bir çağrı ile aynı şeyi yazmak için bir yol varsa merak, bir şekilde işleme sırasına parametrizasyonunu? Bunun gibi (tabii ki C++ 0x ile bile çalışmıyor ki çünkü başlayacak() ve rbegin() farklı türde dönüş):

auto iter = flag ? indices.begin() : indices.rbegin(); 
    auto end = flag ? indices.end() : indices.rend(); 

    BOOST_FOREACH (int i, std::make_pair(iter, end)) 
    { 
     process(i); 
    } 

cevap

5

Ne isterseniz Boost.Variant ile uygulanabilir.

fikri yineleyici ya bir ön ihtiva eden bir varyant (steroid bir Cı birlik gibi düşünmek) depolayan Yineleyici yeni bir tür tanımlamak ya da tersine çevirmek için olan:

template<class InputRange> 
struct any_dir_iterator 
: std::iterator_traits<typename boost::range_iterator<InputRange>::type> { 

    typedef typename boost::range_iterator<InputRange>::type forward_iterator; 
    typedef typename 
     boost::range_reverse_iterator<InputRange>::type reverse_iterator; 

    typedef boost::variant<forward_iterator, reverse_iterator> iterator_type; 

    iterator_type current_it, end_it; 

    any_dir_iterator(InputRange & input_range, 
        bool fwd = true, 
        bool end = false) 
    { 
     end_it = fwd ? iterator_type(boost::end(input_range)) 
        : iterator_type(boost::rend(input_range)); 

     if(end) 
      current_it = end_it; 
     else 
      current_it = fwd ? iterator_type(boost::begin(input_range)) 
          : iterator_type(boost::rbegin(input_range)); 
    } 

    reference operator*() const { 
     return boost::apply_visitor(dereference_visitor<any_dir_iterator>(), 
            current_it); 
    } 

    any_dir_iterator & operator++() { 
     boost::apply_visitor(increment_visitor<any_dir_iterator>(), 
          current_it); 
     return *this; 
    } 

    bool operator==(any_dir_iterator const & rhs) { 
     return boost::apply_visitor(equals_visitor<any_dir_iterator>(), 
            current_it, rhs.current_it); 
    }  
}; 

Bu Adobe's any iterator benzer ancak Çok daha az genel, yani normal bir tekrarlayıcıya kıyasla neredeyse hiç performansa sahip olamayacağı anlamına geliyor.

template<class AnyDirIterator> 
struct dereference_visitor 
: boost::static_visitor<typename AnyDirIterator::iterator_type> { 

    typedef typename AnyDirIterator::reference result_type; 

    template<class FwdOrRevIterator> 
    result_type operator()(FwdOrRevIterator const & it) const { 
     return *it; 
    } 
}; 

template<class AnyDirIterator> 
struct increment_visitor 
: boost::static_visitor<typename AnyDirIterator::iterator_type> { 

    typedef void result_type; 

    template<class FwdOrRevIterator> 
    result_type operator()(FwdOrRevIterator & it) const { 
     ++it; 
    } 
}; 

template<class AnyDirIterator> 
struct equals_visitor 
: boost::static_visitor<typename AnyDirIterator::iterator_type> 
{ 
    typedef bool result_type; 

    template <typename FwdOrRevIterator> 
    bool operator()(FwdOrRevIterator const & lhs, 
        FwdOrRevIterator const & rhs) const { 
     return lhs == rhs; 
    } 

    template <typename T, typename U> 
    bool operator()(const T &, const U &) const { 
     return false; // comparing fwd to rev or vice-versa 
    } 
}; 

zor kısmı oldu: Yukarıdaki kodda görebileceğiniz gibi

, tüm mantık aşağıdaki gibi tanımlar statik ziyaretçilere devredilmektedir.

template<class InputRange> 
boost::iterator_range<any_dir_iterator<InputRange> > 
make_any_dir_range(InputRange & range, bool forward=true) { 
    typedef any_dir_iterator<InputRange> iterator; 
    return boost::make_iterator_range(iterator(range, forward), 
             iterator(range, forward, true)); 
} 

Ve hepsi bu: Ama yine de kendisi için biz Boost.Range kütüphanesi tarafından sağlanan işlevselliği dayanan bir yardım edicinin, kullanmak için bu daha uygun yapmak zorunda. Şimdi yazabilirsiniz:

int main() { 

    int items[] = { 1, 2 }; 
    typedef std::vector<int> container_type; 
    container_type container(items, items + sizeof(items)/sizeof(items[0])); 

    BOOST_FOREACH(int i, make_any_dir_range(container, true)) 
     std::cout << i << " "; 

    std::cout << "\n"; 
    BOOST_FOREACH(int i, make_any_dir_range(container, false)) 
     std::cout << i << " "; 

    return 0; 
} 

yazdırır Hangi:

Ben main işlevi ihtimalini gösterilmemiş olmasına rağmen bu, hem de const kaplarda ile çalışır
1 2 
2 1 

.

Boost.Range kullanılarak elde edilen bir başka güzel şey, bunun kutudan çıkan dizilerle çalışmasıdır. Yani bunu yapabilirsiniz:

int items[] = { 1, 2 }; 

BOOST_FOREACH(int i, make_any_dir_range(items, true)) // Prints "1 2" 
    std::cout << i << " "; 

Çok Ben Uygulanmayan bir şeyler bıraktım Bu cevap kısa tutmak (fakat yeni ziyaretçiler gerektirmeyen, bütün klişe konum):

  • postfix artım operatörü
  • = operatör
  • -> operatörü

İşte all the code in Codepad bu. "Hata olarak uyarıları işleme" ilkesi nedeniyle, Codepad bunu yutmaz, ancak VS2008 ve GCC 4.4, yerel makinemde tamamlar.

GÜNCELLEME

bazı testler yaptık ve görünüşe boost::variant bazı çalışma zamanı yükü tanıtmak kapsamaz: main işlevinde gibi bir BOOST_FOREACH tabanlı döngü çalışır yaklaşık 4 kat daha yavaş (sürüm modunda derlenmiş olduğunda) Düz bir yineleyici kullanarak eşdeğer sürümden. Bu, Adobe'nin any_iterator tarafından getirilen yükten daha iyi veya kötü olup olmadığını kontrol etmek ilginç olurdu.

+0

Vay, bu beklediğimden daha fazla bir şey;) Acaba iyi bir birlikteliğe sahip çözümün mümkün olup olmayacağını merak ediyorum. Ama ben zaten bu karmaşık kodu tanıtmaktan daha iyi bir şey olduğunu düşünüyorum. Sürekli böyle bir şey yapıyor olsaydım buna değer olabilirdi, ama sadece birkaç yerde. Yine de teşekkürler! –

+1

@Alex - Sendikaların burada kullanılabileceğini düşünmüyorum, şuna bakın: http://stackoverflow.com/questions/943267/is-it-a-good-practice-to-use-unions-in-c/ 943611 # 943611 – Manuel

1

Eh bariz kimse bu mantığını işleyen bir sınıf haline getirmek durum, ya bir bayrak saklamak ya da polimorfizm kullanarak. Ancak, en iyi if bildirimini "gizlemek" olacaktır.

+0

Bu durumu ele alma mantığını kaba makro kullanmadan çizebilir misiniz? Gördüğüm şekilde, bu sınıfın içinde, iki farklı döngüde işlenmek üzere iki (dolaylı) çağrı olacak. –

+0

Uygulama için şablonlar kullanabilirsiniz (bir döngü, iki senaryo). * Çalıştırma süresinde * nasıl kullanabileceğinizi göremiyorum * her iki yineleyici için mantığı kodlamadan kullanmadan önce kullanacağınız yineleyici türlerini (makrolarda bile). – UncleBens