Sign in to follow this  
password

std::vector question

Recommended Posts

If I create a two dimensional std::vector (4x4) like this: vector <vector<int> > LBlock(4, vector<int>(4)); Can I somehow initialize the elements in the vector like a normal array like this? I have to do this in order to get the order of the 0's and 1's the way I want it.
LBlock = {{0, 0, 0, 0},
          {0, 0, 0, 0},
          {0, 0, 0, 0},
          {0, 0, 0, 0}};

Question #2 Can I allot a two dimensional vector to a two dimensional array somehow. If this works, I don't have to worry about initializing the vectors the way I mentioned above. Neither vecBlock = arrBlock; or type casting works, is there any other way?

Share this post


Link to post
Share on other sites
(1) Is there a reason you don't want to use the standard push_back function to initialise your vector?
  
for( int firstIndex = 0; firstIndex < 4; firstIndex++ )
{
for( int secondIndex = 0; secondIndex < 4; secondIndex++ )
{
(LBlock[firstIndex]).push_back( 0 );
}
}


(2) Depending on the size of your arrays and vectors you could just loop through the elements.

Cheers
Amos

Share this post


Link to post
Share on other sites
[EDIT - Sorry Amos - you beat me to it [smile]]

I may be wrong, but I am pretty sure that the answer is no to both of those questions.

vector data is allocated on the heap, even when the vector itself is static, so if nothing else, that would preclude being able to initialise it with the syntax above.

And I am not aware of any implicit conversion from a 2D array to a vector of vectors. For a start, they have different properties - a 2D array is always rectangular but a vector of vectors is not.

It is fairly trivial to write such a converter though, so you could initialise a 2D array statically then, on start up, use a function to copy this into a vector of vectors.


int Data[3][3]={{ 0,1,0 },
{ 1,0,1 },
{ 0,1,0 }};

std::vector<std::vector<int> > Vec;

void Init()
{
Vec.resize(3);

for(int Y=0;Y<3;++Y)
{
for(int X=0;X<3;++X)
{
Vec[Y].push_back(Data[Y][X])); // or is that Data[X][Y]? I can never remember
}
}
}







I think that is right. I get a bit confused by 2D arrays and avoid them like the plague to be honest.

If your container is always going to be rectangluar, I understand that there is a boost::multidimensional_array (or something) that may be useful. Alternatively, here's a class I use for 2D game maps:


class Map
{
private:
std::vector<int> V; int W,H;

public:
Map(int Width,int Height) : W(Width),H(Height) { V.resize(W*H); }

int &operator()(int X,int Y);
int operator()(int X,int Y) const;

int Width() const { return W; }
int Height() const { return H; }

class BoundsError{ };
};

int &Map::operator()(int X,int Y)
{
if(X<0 || X>=W || Y<0 || Y>=H) throw BoundsError();
return V[(Y*W)+X];
}

int Map::operator()(int X,int Y) const
{
if(X<0 || X>=W || Y<0 || Y>=H) throw BoundsError();
return V[(Y*W)+X];
}

int main()
{
Map map(10,10);

map(3,2)=23;

std::cout << map(9,4) << std::endl; // etc
}





This approach maintains the invariant that your container is rectangular and it also guarantees that the data is contiguous in memory, which a vector of vectors does not.

HTH Paul

Share this post


Link to post
Share on other sites
Quote:
Can I somehow initialize the elements in the vector like a normal array like this?


No, only normal arrays can be initialized thus, although there is discussion about extending C++ in this direction.

Quote:
Can I allot a two dimensional vector to a two dimensional array somehow.


No. For starters you don't have a 2D vector, but a vector of vectors, each of which exists independently and not really as an integral part of a 2D structure. The fact that those vectors have been themselves put in a vector is completely incidental from their point of view.

There do exist true multidimensional array classes, like boost::multi_array. Also, depending on how exactly you plan on using the data, the std::valarray class may be what you are looking for.

Share this post


Link to post
Share on other sites
I didn't realize I could put the push_backs into a function like that. That's probably the easiest way I can make it. Something like this:


void SetPiece(int Data[]) {
Vec.resize(3); // not sure what this line does though, can only guess :)

for(int Y=0;Y<4;++Y) {
for(int X=0;X<4;++X) {
piece[Y].push_back(Data[Y][X]));
}
}
}

SetPiece(LBlock);



Probably have to change it to a vector with vector's though, since i've used that in the program so far. I hope they extend the language and makes this possible though, I find it really useful sometimes. Thank you all.

Share this post


Link to post
Share on other sites
you could always use std::fill or std::fill_n:

http://support.roguewave.com/support/docs/sourcepro/edition9/html/stdlibref/fill.html

also, it might be "better" to just have a 1D vector and index it by ( x + MAX_Y * y ); nested vectors of the same type is just a lot of overhead for no real additional functionality.

-me

Share this post


Link to post
Share on other sites
Plus if you're going to be initialising your vector of vectors because you already know its dimensions do you really need a vector or would a 2D array suit your needs better?

Cheers
Amos

Share this post


Link to post
Share on other sites
Quote:
Original post by password
Vec.resize(3); // not sure what this line does though, can only guess :)


Sets the size of the vector to 3. If there were originally fewer elements in the vector, the new elements are default-initialized. If there were more, the vector gets truncated.

Quote:
Probably have to change it to a vector with vector's though, since i've used that in the program so far.


Incidentally, if you're only going to use the vector as a static array, you may be better off just using an array in the first place, or grab a STL array wrapper like boost::array or std::tr1::array (where available).


Quote:
I hope they extend the language and makes this possible though, I find it really useful sometimes. Thank you all.


If you are curious, check out the current discussion. For more, go here and scroll down to or search for the papers about "Initializer lists".

[Edited by - Fruny on October 9, 2006 5:58:41 PM]

Share this post


Link to post
Share on other sites
Hey I dont got any live code but I can tell you somethin, im making a game that is using a PLETHORA of multidimentional[sic] vectors in it.
The way I make a normal vector behave like a multidimensional one is to create a width variable and use that to controll moving "up" and "down".
Heres the logic like so.


vector <int> board(9, 0);

for (int i = 0;i < board.size(); i++)
display board[i];

This gets displayed.
000000000

int boardWidth = 3;

int j = 1;
for (int i = 0;i < board.size(); i++)
{
display board[i];
j++;
if (j == boardWidth)
newline;
}

This gets displayed.

000
000
000


if you wanted to move something up or down on the board, you simply add or subtract the width of the board.

if you want to use X and Y coordinates heres an little algorithm I made that will convert a vector location into X and Y coordinated, and another one that will take X and Y coordinated and convert them into a vector spot.

Here this takes a vector location and makes it into x and y coordinated.

int x = vector loacation + 1;
int y = 0;

while (x > boardWidth)
{
x -= boardWidth;
y++;
}

This will take X and Y coordinates and return a vector location.

int XY(int x, int y, boardWidth)
{
x--;
y--;
while (y > 0)
{
y--;
x += boardWidth;
}

return x;
}

so you could do...

board[XY(1,1,3)] = 1;
or
board[5] = 1;

both are the same thing.

Hope I was of some assistance.

Share this post


Link to post
Share on other sites
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;
}

Share this post


Link to post
Share on other sites
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()

Share this post


Link to post
Share on other sites
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);
}

Share this post


Link to post
Share on other sites
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][i] << 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][i] << 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
....

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this