std::vector question

Started by
17 comments, last by password 17 years, 6 months ago
You could just wrap the whole thing up in a class like so.
template<typename T>class Array2D{    std::vector<T> vec;    int Width, Height;public:    Array2D()  : Width(0), Height(0)    {}    Array2D(int W, int H)  : Width(W), Height(H)    {        vec.resize(Width*Height);            }    void Resize(int W, int H)     {        Widht = W;        Hwight = H;        vec.resize(Width*Height);    }    T& Index(int row, int column)    {        return Vec[column*Width + row]    }};

Probably want to add some bounds checking to Index()
Advertisement
Quote:Original post by EasilyConfused
Not meaning to knock your code, Kaanin, but your functions to convert could be far more simply written and avoid loops all together:

int XYtoIndex(int X,int Y,int Width){    return (Y*Width)+X;}void IndextoXY(int Index,int Width,int &X,int &Y){    X=Index%Width;    Y=Index/Width;}


Ew, reference parameters for something like that?

typedef std::pair<int, int> xy;inline int XYtoIndex(xy XY, int width) {  return (XY.second * Width) + XY.first;}inline int XYtoIndex(int x, int y, int width) {  return XYtoIndex(std::make_pair(x, y), width);}inline xy IndextoXY(int index, int width) {  return std::make_pair(index % width, index / width);}
[grin]

It was just an example (mutter mutter).
I have one last problem with vectors right now. I want to push_back a 2d array into a vector just like you would push_back a class into a vector.

This works fine if I add a pointer to an int as datatype.
int arr[4] = {45,1,22,7};    vector<int*> Vec;Vec.push_back(arr);    for (int i=0;i<4;i++)    cout << Vec[0] << endl;


The same program using two dimensional array doesn't work however.
int arr[4][4] = {{45,1,22,7},                 {1,3,3,7},                 {0,55,123,44},                 {95,13,52,13}};    vector<int*> Vec;Vec.push_back(arr);    for (int i=0;i<4;i++)    cout << Vec[0] << endl;


Using a one dimensional array is not an option in this case, since the whole program is made to work with 2d arrays. Containing the arrays themselves shouldn't be a problem, but the compiler doesn't agree with me. Any way to contain 2d arrays with vector so I can refer to the arrays like this?

Vec[0] = 2d array 1
Vec[1] = 2d array 2
Vec[2] = 2d array 3
....
Doubtful. When you define the type of a 2D array, you have to provide the size of the second subscript, so that the compiler can do the necessary math to resolve the index when you dereference:

int f(int a[][3]){    cout << a[1][2]; // couldn't work this out without the 3 above}


I just tried:

std::vector<int[][3]> v;


and got a load of "not an allowed type" errors.

The thing is that in your first example with a 1D array vector, you are not really pushing a 1D array into the vector, you are just pushing a pointer to the start of the array. The array name decomposes down to a pointer to the start of the array when it is passed as a parameter of such type to push_back.

A pointer can be used pretty much interchangabley with a 1D array. The same is not true of a 2D array so I think you are out of luck.

I'm not sure if you will like this, but the following code shows how you can create a class that uses a single normal pointer to memory but allows for access via [][] like a 2D array. You could use a technique like this, or find a third-party container (boost?) that does a similar thing, then "silently" replace all your 2D arrays at declaration with such a class so the rest of your code is not affected:

#include <iostream>class array{private:    int *p,w,h;class sub{private:    array *owner; int i;public:    sub(array *a,int v) : owner(a),i(v) { }    int &operator[](int v){ return owner->val(i,v); }};public:    array(int wt,int ht) : w(wt),h(ht) { p=new int[w*h]; }    ~array(){ delete [] p; }    int &val(int x,int y){ return p[(y*w)+x]; }    sub operator[](int i){ return sub(this,i); }};int main(){    array a(3,3);    int i=0;    for(int y=0;y<3;++y)        {        for(int x=0;x<3;++x)            {            a[x][y]=i++;            }        }    for(int y=0;y<3;++y)        {        for(int x=0;x<3;++x)            {            std::cout << a[x][y] << " ";            }        std::cout << std::endl;        }    return 0;}


With something like that, you could certainly have a std::vector<array>, although you'd need to supply array with a default constructor and a resize method so you could resize the individual arrays after the vector had been initialised.

You could then do:

std::vector<array> v;v.resize(1);v[0].resize(3,3);v[0][1][2]=3;


However, just because something is possible doesn't mean it is a good design [smile].

HTH Paul
Quote:Original post by EasilyConfused
Doubtful. When you define the type of a 2D array, you have to provide the size of the second subscript, so that the compiler can do the necessary math to resolve the index when you dereference:

int f(int a[][3]){    cout << a[1][2]; // couldn't work this out without the 3 above}


I just tried:

std::vector<int[][3]> v;


and got a load of "not an allowed type" errors.

The thing is that in your first example with a 1D array vector, you are not really pushing a 1D array into the vector, you are just pushing a pointer to the start of the array. The array name decomposes down to a pointer to the start of the array when it is passed as a parameter of such type to push_back.

A pointer can be used pretty much interchangabley with a 1D array. The same is not true of a 2D array so I think you are out of luck.

I'm not sure if you will like this, but the following code shows how you can create a class that uses a single normal pointer to memory but allows for access via [][] like a 2D array. You could use a technique like this, or find a third-party container (boost?) that does a similar thing, then "silently" replace all your 2D arrays at declaration with such a class so the rest of your code is not affected:

*** Source Snippet Removed ***

With something like that, you could certainly have a std::vector<array>, although you'd need to supply array with a default constructor and a resize method so you could resize the individual arrays after the vector had been initialised.

You could then do:

std::vector<array> v;v.resize(1);v[0].resize(3,3);v[0][1][2]=3;


However, just because something is possible doesn't mean it is a good design [smile].

HTH Paul


Thank you for writing all that code up. But from what it sounds from the last thread when I asked for something else, this thing that i'm about to do should be quite simple.

I'm currently making a Tetris game and want to make rotation for the blocks. So far, this is how it looks.

// the current piece (4x4)vector<vector<int> > piece(4, vector<int>(4));// the blocks i'm using in their first form    int LBlock0[4][4] = {{0, 1, 0, 0},                         {0, 1, 0, 0},                         {0, 1, 0, 0},                         {0, 1, 1, 0}};    int IBlock0[4][4] = {{0, 1, 0, 0},                         {0, 1, 0, 0},                         {0, 1, 0, 0},                         {0, 1, 0, 0}};             int LBlockMirror0[4][4] = {{0, 0, 1, 0},                               {0, 0, 1, 0},                               {0, 0, 1, 0},                               {0, 1, 1, 0}};        int SquareBlock0[4][4] = {{0, 0, 0, 0},                              {0, 1, 1, 0},                              {0, 1, 1, 0},                              {0, 0, 0, 0}};    int ArrowBlock0[4][4] = {{1, 1, 1, 0},                             {0, 1, 0, 0},                             {0, 0, 0, 0},                             {0, 0, 0, 0}};


Going from an advice from another thread, I will make the rotation by changing the index of an array that holds all the blocks of the current kind. My way of solving this is to make a global variable named "rotation" which is going to have a value between 0-3 which represents the index/rotation of the current block.

Then I thought I could do it some way like this. LBlock[rotation]; supposed the LBlock vector/array contains all the 4 different forms of the LBlock.

I also have a switch statement in the function that generates a new block. That switch checks which block is going to be next. If the switch is for example 0 (random number), the currentblock is set to LBlock[0]; where 0 is the normal rotation of the block. When I later on Blit the blocks on the screen I use the rotation variable like this, LBlock[rotation]. rotation=0 if the player hasn't pushed the up button which changes the value.

The reason I mentioned the switch case is because I want to somehow set the vector to the block like this:

Vec = LBlock[0];
Vec = SquareBlock[0];
...

Well, anyways, the important part is how to store the 2d arrays somewhere so I can use them like I mentioned in combination with the rotation variable.

How would you make this in a simple way? I realized I should've asked this instead, because my first attempt may be too odd where I wanted to store 2d arrays in vectors.
It has just occured to me, and I should have suggested this before really - you can't have a vector of 2D arrays, but you can have a vector of structs containing 2D arrays:

#include <iostream>#include <vector>struct Block{Block(int D[][3]);int Data[3][3];};Block::Block(int D[][3]){    for(int Y=0;Y<3;++Y)        {        for(int X=0;X<3;++X)            {            Data[X][Y]=D[X][Y];            }        }}typedef std::vector<Block> Blocks;std::vector<Blocks> v;int B[3][3]={ { 1,1,1 },              { 1,0,0 },              { 1,0,0 } };int main(){    v.resize(8);    v[0].push_back(Block(B));    for(int Y=0;Y<3;++Y)        {        for(int X=0;X<3;++X)            {            std::cout << v[0][0].Data[X][Y] << " ";            }        std::cout << std::endl;        }    return 0;}


Perhaps you can base your solution round that, although it will involve some changes to your existing code.
One option is to use an array class wrapper of sorts (Boost has some).
Another option is to wrap the 2D array representing the block into an object, and store that instead:

class Block{  public:    // ...etc...  private:    int  mData[4 * 4];  // Could be [4][4] if you really want...};


Then use a std::vector of Block. This is a much more robust solution than trying to store raw arrays into a vector, since it isolates the implementation detail of a block (with appropriate accessors, such as an overloaded operator() or a get(x,y) method, you can implement the underlying storage however you like - an array, like I did, a 2D array, another vector, whatever). It also allows you to expand block to include extra methods and properties without having to create parallel-storage vectors. You could even move the information about the blocks rotations -- all of which can be calculated directly from a template block in a fixed orientation -- into the Block class and thus remove the need for the original vector (which seems geared towards storing just block rotations) entirely.
That will indeed work, good ideas. I actually had a similar idea, but never followed up on it for some reason. I realized now it will work though.

This topic is closed to new replies.

Advertisement