Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I made a class to convert the index between 2D and 1D arrays. For example, between

{1,3,5,7,9,11} and {{1,3},{5,7},{9,11}}

  • When i is 0, xy should be (0,0)
  • When i is 1, xy should be (0,1)
  • When i is 2, xy should be (1,0)

And so on...

I would be able to convert between the i-index and the xy-index to find get the equivalent location. I feel like this piece of code may be too long for its purpose.

class IndexConv {
    public:
    IndexConv(int rows, int cols);
    bool check_i(int i) const;
    bool check_xy(int x, int y) const;
    pair<int, int> to_xy(int i) const;
    int to_i(int x, int y) const;
    private:
    const int rows_;
    const int cols_;
};

IndexConv::IndexConv(int rows, int cols)
    :rows_(rows), cols_(cols) {}

bool IndexConv::check_i(int i) const
{
    return i>=0 || i<rows_*cols_;
}

bool IndexConv::check_xy(int x, int y) const
{
    return x>=0 && y>=0 && x<rows_ && y<cols_;
}
pair<int, int> IndexConv::to_xy(int i) const
{
    if(!check_i(i)) throw out_of_range("IndexConv: i out of bounds.");
    int x = i/cols_;
    int y = i-x*cols_;
    return make_pair(x,y);
}

int IndexConv::to_i(int x, int y) const
{
    if(!check_xy(x,y)) throw out_of_range("IndexConv: xy out of bounds.");
    int i = x*cols_+ y;
    return  i;
}
share|improve this question

Use modulus

Instead if this i-x*cols_ you can use the modulus operator i % cols_.

Use a unsigned type.

If you use unsigned there is no need to check for x >= 0 && y >= 0. Also you can con throw the exception directly in check. And you just need check_xy.

    void IndexConv::check(unsigned x, unsigned y) const
    {
        if(x >= rows_ || y >= cols_) 
            throw std::out_of_range("IndexConv: parameter out of bounds.");
    }

    pair<int, int> IndexConv::to_xy(unsigned i) const
    {
        int x = i / cols_;
        int y = i % cols_;
        check(x, y);
        return make_pair(x, y);
    }

    int IndexConv::to_i(unsigned x, unsigned y) const
    {
        check(x, y);
        return x * cols_ + y;
    }

EDIT: Changed the answer to keep check_xy. Keeping check_i can map (0,2) to 2 when it should throw an exception (Following your example).

share|improve this answer
    
Do you still need to return true in the check function? – ijklr Feb 23 at 21:30
    
no that's a mistake, edited. – MAG Feb 23 at 23:17

In addition to the excellent suggestions by @MAG, I suggest the following:

A better class name

I suggest using a full name rather than a shorter name - IndexConverter.

No need to use const in member variables

I don't see any benefit of using const member variables. You can simply use (I am incorporating the suggestion to use an unsigned type):

unsigned int rows_;
unsigned int cols_;

Define a size type

To further enhance consistency in the class, define a size type in your class and use it.

class IndexConverter
{
   public:

      using size_type = unsigned int;

      IndexConverter(size_type rows, size_type cols);
      bool check_i(size_type i) const;
      bool check_xy(size_type x, size_type y) const;
      pair<size_type, size_type> to_xy(size_type i) const;
      size_type to_i(size_type x, size_type y) const;
   private:
      size_type rows_;
      size_type cols_;
};

This allows you to change the type used for size with minimal disruption.

Use std::pair instead of pair

Your use of

pair<int, int> to_xy(int i) const;

works presumably because you have added

using namespace std;

in the .h file before the definition of the class. See Why is “using namespace std;” considered bad practice?.

Remove the using line and use std::pair.

Better names for to_i and to_xy

I would suggest using to_1d_index and to_2d_index instead.

In the same vein, I would suggest using check_1d_index and check_2d_index. If you follow the suggestion by @MAG, then you need just check_2d_index.

Add an overload of to_1d_index

      size_type to_1d_index(std::pair<size_type, size_type> xy) const;

This is a convenience so that you can use something like:

IndexConverter c(10, 20);
unsigned int i = 2;
auto 2d_index = c.to_2d_index(i);
auto j = c.to_1d_index(2d_index);

and expect to see j == i.

Adding whitespaces

Adding a blank line between function declarations makes it easier to read. That could be my personal taste! YMMV.

Final Suggested Version

class IndexConverter
{
   public:

      using size_type = unsigned it;

      IndexConverter(size_type rows, size_type cols);

      void check_2d_index(size_type x, size_type y) const;

      std::pair<size_type, size_type> to_2d_index(size_type i) const;

      size_type to_1d_index(size_type x, size_type y) const;

      size_type to_1d_index(std::pair<size_type, size_type> xy) const;

   private:
      size_type rows_;
      size_type cols_;
};
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.