2

1D 配列 (テンプレート) として表されるグリッドであるコンテナーを設計しています。ここにコードの抜粋を投稿していますが、実際にはもっと多くのコードがあります。これは、各セルが世界の小さな領域を表すロボット アプリケーションのローリング占有グリッドとして使用されます。

私がそのグリッドで頻繁に行う操作の 1 つは、すべてのセルを調べて、それらのワールド座標も取得することです。

for( unsigned r=0; r<mygrid.rows_; ++r ) {
  for( unsigned c=0; c<mygrid.cols_; ++c ) {
    cell = mygrid.getRC(r,c);
    mygrid.rcToXY(r,c,&x,&y);
  }
}

これらすべてを格納するイテレータが必要です: セル、その rc 座標、およびその xy 座標。

for( Grid<CellType>::const_iterator it=mygrid.begin(); it!=mygrid.end(); ++it ) {
  cell = *it;
  printf("%d %d %f %f\n", it.r(), it.c(), it.x(), it.y());
}

オンラインの多数の回答とチュートリアルに続いて、次の実装を思いつきました。しかし、私には少し不器用に思えます。学術的な理由から、見栄えを良くしたいと考えています。また、STL互換性は素晴らしいでしょう。

template <class G, typename C>
class base_iterator
{
private:
  G* grid_;
  C* cell_;
  unsigned r_, c_;  // local
  double  x_, y_;

// this should be private with access for friends (Grid) only
// but I can't make it work
public:
  base_iterator(G* grid, unsigned r, unsigned c) : grid_(grid), r_(r), c_(c)
  {
    cell_ = ( r<grid->rows_ && c<grid->cols_ ) ? &grid_->getRC(r,c) : 0;
    grid_->rcToXY(r,c,&x_,&y_);
  }


public:
  base_iterator() : grid_(0) { }

  // used to cast an iterator to a const_iterator
  template <class G2, typename C2>
  base_iterator(const base_iterator<G2,C2>& other)
  {
    grid_ = other.grid();
    cell_ = & other.cell();
    r_ = other.r();
    c_ = other.c();
    x_ = other.x();
    y_ = other.y();
  }

  // this should be private with access for friends only
  G* grid() const { return grid_; }

  C& cell() { return *cell_; }
  const C& cell() const { return *cell_; }
  unsigned r() const { return r_; }
  unsigned c() const { return c_; }
  double  x() const { return x_; }
  double  y() const { return y_; }

  C* operator->() { return cell_; }
  const C* operator->() const { return cell_; }

  C& operator*() { return *cell_; }
  const C& operator*() const { return *cell_; }

  //prefix
  base_iterator & operator++()
  {
    // my iteration logic here which needs access to grid
    // in order to find the number of rows, etc.
    return *this;
  }

  //postfix
  base_iterator operator++(int)
  {
    base_iterator it(*this);   // make a copy for result
     ++(*this);              // Now use the prefix version to do the work
     return it;          // return the copy (the old) value.
  }

  template <class G2, typename C2>
  bool operator==(const base_iterator<G2,C2> & other) const
  {
    return cell_ == &other.cell();
  }

  template <class G2, typename C2>
  bool operator!=(const base_iterator<G2,C2>& other) const
  { return cell_!=other.cell(); }
};

そして、私のグリッドクラスで:

  typedef base_iterator<Grid<T>,T> iterator;
  typedef base_iterator<Grid<T> const, T const> const_iterator;

  iterator begin() { return iterator(this,0,0); }
  iterator end() { return iterator(this,rows_,cols_); }

  const_iterator begin() const { return const_iterator(this,0,0); }
  const_iterator end() const { return const_iterator(this,rows_,cols_); }

繰り返しますが、これは機能しますが、少しぎこちなく感じます (反復子コードのコメントを参照)。どうすれば改善できるか知りたいです。ブースト イテレータ ファサードまたはアダプタの使用に関する多数の投稿を見ましたが、それを自分のケースに適応させる方法がわかりませんでした。

4

1 に答える 1

1

さて、私は非常に満足している1つの解決策を見つけました。参照用の完全なリストは次のとおりです。特に、クラス宣言の外で実装を行えるようにするために、時間がかかったトリッキーな部分がいくつかあります。あちこちで読んだことによると、クラス base_iterator をネストされていないグリッドのクラスにすることができませんでした。それは不可能だと思います。

#include <cstdio>
#include <cassert>
#include <stdexcept>
#include <algorithm>


template <class T>
class Grid
{
public:
  // these should be private, with public getters...
  double resolution_;
  unsigned rows_, cols_;
  int map_r0_, map_c0_; // grid coordinates of origin of map

private:
  T* cell_;

public:
  Grid(double resolution, unsigned rows, unsigned cols);
  ~Grid() { delete[] cell_; }

  T & getRC(unsigned r, unsigned c);
  const T & getRC(unsigned r, unsigned c) const;
  void rcToXY(unsigned r, unsigned c, double* x, double* y) const;

public:
  template <class GridType, class CellType>
  class base_iterator : std::iterator<std::forward_iterator_tag, CellType>
  {
  private:
    friend class Grid;
    GridType* grid_;
    CellType* cell_;
    unsigned r_, c_;  // local
    double  x_, y_;

    base_iterator(GridType* grid, unsigned r, unsigned c);

  public:
    base_iterator() : grid_(0) { }

    template <class G2, class C2>
    base_iterator(const base_iterator<G2,C2>& other);

    CellType& cell() { return *cell_; }
    const CellType& cell() const { return *cell_; }
    unsigned r() const { return r_; }
    unsigned c() const { return c_; }
    double  x() const { return x_; }
    double  y() const { return y_; }

    CellType* operator->() { return cell_; }
    const CellType* operator->() const { return cell_; }

    CellType& operator*() { return *cell_; }
    const CellType& operator*() const { return *cell_; }

    //prefix
    base_iterator & operator++();

    //postfix
    base_iterator operator++(int);

    template <class G2, class C2>
    bool operator==(const base_iterator<G2,C2> & other) const
    { return cell_ == other.cell_; }

    template <class G2, class C2>
    bool operator!=(const base_iterator<G2,C2>& other) const
    { return cell_ != other.cell_; }
  };

  typedef base_iterator<Grid<T>,T> iterator;
  typedef base_iterator<Grid<T> const, T const> const_iterator;

  iterator begin() { return iterator(this,0,0); }
  iterator end() { return iterator(this,rows_,0); }

  const_iterator begin() const { return const_iterator(this,0,0); }
  const_iterator end() const { return const_iterator(this,rows_,0); }
};


template <class T>
Grid<T>::Grid(double resolution, unsigned rows, unsigned cols)
  : resolution_(resolution), rows_(rows), cols_(cols), map_r0_(0), map_c0_(0)
{
  cell_ = new T[rows_*cols_];
}

template <class T>
T & Grid<T>::getRC(unsigned r, unsigned c)
{
  if (r >= rows_ || c >= cols_)
    throw std::runtime_error("Out of bounds");
  return cell_[r * cols_ + c];
}

template <class T>
const T & Grid<T>::getRC(unsigned r, unsigned c) const
{
  if (r >= rows_ || c >= cols_)
    throw std::runtime_error("Out of bounds");
  return cell_[r * cols_ + c];
}

template <class T>
void Grid<T>::rcToXY(unsigned r, unsigned c, double* x, double* y) const
{
  *x = (map_c0_ + c + 0.5) * resolution_;
  *y = (map_r0_ + r + 0.5) * resolution_;
}


template <class T>
template <class GridType, class CellType>
Grid<T>::base_iterator<GridType,CellType>::base_iterator(GridType* grid, unsigned r, unsigned c)
: grid_(grid), r_(r), c_(c)
{
  if( r<grid->rows_ && c<grid->cols_ ) {
    cell_ = &grid_->getRC(r,c);
    grid_->rcToXY(r,c,&x_,&y_);
  }
  else
    cell_ = &grid_->getRC(grid->rows_-1,grid->cols_-1) + 1;
}

// beautiful triple template declaration !
template <class T>
template <class GridType, class CellType>
template <class G2, class C2>
Grid<T>::base_iterator<GridType,CellType>::base_iterator(const Grid<T>::base_iterator<G2,C2>& other)
{
  grid_ = other.grid_;
  cell_ = other.cell_;
  r_ = other.r();
  c_ = other.c();
  x_ = other.x();
  y_ = other.y();
}

template <class T>
template <class GridType, class CellType>
Grid<T>::base_iterator<GridType,CellType> & Grid<T>::base_iterator<GridType,CellType>::operator++()
{
  assert( grid_!=0 );

  if( c_==grid_->cols_-1 )
  {
    c_ = 0;
    x_ = (grid_->map_c0_ + 0.5) * grid_->resolution_;
    ++r_;
    y_ += grid_->resolution_;
  }
  else
  {
    ++c_;
    x_ += grid_->resolution_;
  }
  ++cell_;

  return *this;
}

template <class T>
template <class GridType, class CellType>
Grid<T>::base_iterator<GridType,CellType> Grid<T>::base_iterator<GridType,CellType>::operator++(int)
{
  base_iterator it(*this);   // make a copy for result
   ++(*this);              // Now use the prefix version to do the work
   return it;          // return the copy (the old) value.
}

void print(unsigned i)
{
  printf("%d ", i);
}

int main()
{
  Grid<unsigned> mygrid(.1,2,3);
  unsigned ctr=0;
  for( Grid<unsigned>::iterator it=mygrid.begin(); it!=mygrid.end(); ++it )
    *it = ctr++;

  ctr = 0;
  printf("All elements: r, c, x, y, value\n");
  for( Grid<unsigned>::const_iterator it=mygrid.begin(); it!=mygrid.end(); ++it ) {
    assert( *it == ctr++ );
    printf("%d %d %f %f %d\n", it.r(), it.c(), it.x(), it.y(), *it);
  }

  printf("All elements values: ");
  std::for_each(mygrid.begin(), mygrid.end(), print);
  printf("\n");

  return 0;
}
于 2013-03-05T19:08:48.177 に答える