Ragelを使う場合、たいていはイテレータをぽんと渡すことができる。ただし、Scannerを利用すると、イテレータを初期化するために0を代入する(まあ、元々対象としていたのはイテレータではなくポインタだから)。

というわけで、イテレータをラップして0を代入できるようにしよう(ちなみにRagelのソースコード変更するほうが楽だった)。要件を整理すると、Ragelが要求するのは、
  • Bidirectional Iterator (N3242 P.823)
  • a + n (N3242 P.824)
  • a - n (N3242 P.824)
  • a = 0

というわけで、ラップされるイテレータの要件はBidirectional Iteratorとなる。完全なRandom Access Iteratorを作るためにはb - aが必要で、b - aはBidirectional Iteratorから作れない(作れないよね?)けれど、a + nとa - nに限定すればstd::advanceで作れる。

実装にboost::iterator_facadeを使った。boost::bidirectional_traversal_tagを指定したので、ラップするイテレータはBidirectional Iteratorである(と、名乗っている)。だけど、advanceを定義しておけば問題なし、iterator_categoryを気にしないRagelがa + nやa - nという構文を使っても、ちゃんとよきにはからってくれる。

さすが!

【追記】下記のコードは、ラップされるイテレータがRandom Access Iteratorの場合もBidirectional Iteratorを名乗るので、効率が悪くなる場合があるかもしれない。そのうち、真面目にメタるかも。

【追記2】効率が悪くなる(というか遅くなる)例としては、std::distance(begin, end)を呼び出した場合なんか。手元のstd::basic_string(iterator, iterator)の実装では、std::distanceを使って文字数をカウントしてる。

【追記3】b - aが計算できないのは、aとbの順序付けができないからだったはず。逆にb >= aが自明であればもちろん計算できる。

【追記4】ラップされるイテレータのカテゴリをそのまま名乗ることにしてみたコードをおしりに貼った。念のため、SFINAEしといた。

template <typename T, typename = void>
class scanner_iterator;

template <typename T>
class scanner_iterator<
  T,
  typename std::enable_if<
    std::is_base_of<
      std::bidirectional_iterator_tag,
      typename std::iterator_traits<T>::iterator_category
    >::value
  >::type
> : public boost::iterator_facade<
      scanner_iterator<T>,
      typename std::iterator_traits<T>::value_type,
      typename boost::bidirectional_traversal_tag,
      typename std::iterator_traits<T>::reference,
      typename std::iterator_traits<T>::difference_type
    >
{
  friend class boost::iterator_core_access;

public:
  explicit scanner_iterator() : iterator_() {}

  explicit scanner_iterator(T const& iterator) : iterator_(iterator) {}

  // ragel scanner requests semantics (x = 0)
  scanner_iterator& operator=(int rhs) {
    BOOST_ASSERT(rhs == 0);
    iterator_ = T();
    return *this;
  }

private:
  T iterator_;

  typename std::iterator_traits<T>::reference dereference() const {
    return *iterator_;
  }

  bool equal(scanner_iterator const& rhs) const {
    return iterator_ == rhs.iterator_;
  }

  void increment() {
    ++iterator_;
  }

  void decrement() {
    --iterator_;
  }

  // ragel scanner requests semantics (x + n) and (x - n)
  void advance(typename std::iterator_traits<T>::difference_type n) {
    std::advance(iterator_, n);
  }
};

【改訂版】

template <typename T, typename = void>
class scanner_iterator;

template <typename T>
class scanner_iterator<
  T,
  typename std::enable_if<
    std::is_base_of<
      std::bidirectional_iterator_tag,
      typename std::iterator_traits<T>::iterator_category
    >::value
  >::type
> : public boost::iterator_facade<
      scanner_iterator<T>,
      typename std::iterator_traits<T>::value_type,
      typename std::iterator_traits<T>::iterator_category,
      typename std::iterator_traits<T>::reference,
      typename std::iterator_traits<T>::difference_type
    >
{
  friend class boost::iterator_core_access;

public:
  explicit scanner_iterator() : iterator_() {}

  explicit scanner_iterator(T const& iterator) : iterator_(iterator) {}

  // ragel scanner request semantics (x = 0)
  scanner_iterator& operator=(int rhs) {
    BOOST_ASSERT(rhs == 0);
    iterator_ = T();
    return *this;
  }

private:
  T iterator_;

  typename std::iterator_traits<T>::reference dereference() const {
    return *iterator_;
  }

  bool equal(scanner_iterator const& rhs) const {
    return iterator_ == rhs.iterator_;
  }

  void increment() {
    ++iterator_;
  }

  void decrement() {
    --iterator_;
  }

  // ragel scanner request semantics (x + n) and (x - n)
  void advance(typename std::iterator_traits<T>::difference_type n) {
    std::advance(iterator_, n);
  }

  typename std::iterator_traits<T>::difference_type distance_to(
      scanner_iterator const& rhs,
      typename std::enable_if<
        std::is_base_of<
          std::random_access_iterator_tag,
          typename std::iterator_traits<T>::iterator_category
        >::value
      >* = 0) const {
    return std::distance(iterator_, rhs.iterator_);
  }
};

このページへのコメント

3tNJPR <a href="http://kkycnsoteybz.com/">kkycnsoteybz</a>, [url=http://ucjqevophbay.com/]ucjqevophbay[/url], [link=http://rhzkrwuowjcg.com/]rhzkrwuowjcg[/link], http://duwgjfpzfivy.com/

0
Posted by mudrcgt 2013年11月14日(木) 14:44:15 返信

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

メンバーのみ編集できます