Welcome! Log In Create A New Profile

Advanced

declaring an array problem

Posted by g_man 
declaring an array problem
November 08, 2010 01:37AM
I want to declare an array of objects, but the problem is that the object requires two arguments in it's initializer.
ftImage levelNumTxt[12](640,480);
Here is what I want to do. Is there any way to accomplish this?
Re: declaring an array problem
November 08, 2010 02:10AM
you can initialize an array like this

u8 stuff[ 3 ] = { 2, 7, 0xff };

this will give you stuff[ 0 ] = 2, stuff[ 1 ] = 7, stuff[ 2 ] = 0xff

if you cant figure out how to do what you need with these, or if using this method is too much overhead, you can just use pointers.



Edited 1 time(s). Last edit at 11/08/2010 02:14AM by giantpune.
Re: declaring an array problem
November 08, 2010 03:46AM
Thanks, but I'm trying to create an array of ftImages, but there initializer need two arguments. The code above doesn't compile. I was wondering if there is a way to make the staitment.

EDIT:
Ok, I solved the problem with this code:
ftImage *levelNumTxt = (ftImage*) malloc(12*sizeof(ftImage));
	for(i=0;i<20;i++){
		levelNumTxt = ftImage(640,480);
	}	
but, now when I call the code
levelNumTxt.setFont(hyperspd_ttf, hyperspd_ttf_size);
The wii just freezes at a black screen and dolphin complains about compiling at point 0. There isn't any extra warning that come with this line. This only happens for this call. All of the other function calls work. such as:
levelNumTxt.setSize(50);
I know that hyperspd_ttf, and hyperspd_ttf_size are correct. could the problem be in my initialization of levelNumTxt?



Edited 1 time(s). Last edit at 11/08/2010 04:22AM by g_man.
Re: declaring an array problem
November 08, 2010 05:45AM
Well, you're allocating memory for 12 ftImage's, but writing into 20 of them, so you're stomping memory... bad, bad, bad! You should store the number of ftImage's into a variable and then reference that variable (don't use magic numbers); if the number of ftImage's is a constant, then use static memory allocation.
Re: declaring an array problem
November 08, 2010 05:57AM
Sorry that's supposed to be a 12. The number isn't a number in my real code, but I'm simplifying it here.
Re: declaring an array problem
November 08, 2010 09:47AM
The bug might be in different code, is "ftImage" an C++ class? Could you show us that code?

What I'm thinking is happening here is that this:
levelNumTxt = ftImage(640,480);
Might create a new object "ftImage" and then uses the copy constructor, or assign operator to put that in levelNumTxt. But after that the orginal tfImage gets destroyed.
And if you haven't made your own copy constructor then the default is a 100% copy. So if you create a buffer to keep pixels in in the constructor, and free that in the destructor you still get a reference to it.

The best way to make sure C++ objects don't act different then you want is to make a private copy constructor and assign operator.

Add the following code to the start of the class:
class tfImage
{
private:
    tfImage(const tfImage& other) { /* this should never run, trigger some kind of error? */ }
    tfImage& operator = (const tfImage& other) { /* this should never run, trigger some kind of error? */ }


...
Because the copy constructor and assign operator are private then, the context in which they are allowed to be used is very limited. And most likely your code will no longer compile. Which is a good thing ;-)
Re: declaring an array problem
November 08, 2010 03:13PM
Maybe you want to read up on the global placement new function (allows you to specify which memory to use for the new object rather than using dynamic allocation).
Re: declaring an array problem
November 08, 2010 08:28PM
Oh C++ is so much fun!!!

levelNumTxt = ftImage(640,480);

As Daid said, the above will construct a temporary ftImage object, then call ftImage's assignment operator to copy the temporary into levelNumTxt. If you don't write an assignment operator for your class, then the compiler generates one for you; this automatically generated assignment operator does an exact bitwise copy, which is a shallow copy. What this means is that, if your class has data member that points to dynamically allocated memory, then both the temporary object -- ftImage(640,480) -- and object being assigned to -- levelNumTxt -- will have a data member that points to the same dynamically allocated memory. When the temporary object goes out of scope, the temporary object's destructor is called, meaning any dynamically allocated memory it's pointing to is freed, and now levelNumTxt has a member that points to invalid memory.

private:
    ftImage& operator=(const ftImage& other);

As Daid mentioned, you can provide your own operator= in order to prevent the compiler from providing one for you. If you make it private, then you'll get a compiler error if any code outside your class tries to call it; if you just declare it and don't define it (as shown above) then you'll get a compiler error if any code at all (even code inside your class) tries to call it.

ftImage *levelNumTxt = (ftImage*) malloc(12*sizeof(ftImage));
	for(i=0;i<12;i++){
		new (levelNumTxt + i) ftImage(640,480);
	}

As tueidj indicated, you could use placement new (depicted above). This constructs the object at the address given and avoids creating a temporary, but you also have to remember to manually destruct the object when you're done with it. In general, placement new is only for advanced users:
[www.parashift.com]

ftImage levelNumTxt[12];
	for(i=0;i<12;i++){
		levelNumTxt.SetWidthHeight(640,480);
	}

In general you'd want to do the above. This calls ftImage's default constructor for every element in the array, then you loop through and initialize the objects as necessary. But let's assume this is inefficient (perhaps because the ftImage class dynamically allocates memory). Rather than dealing with the headache of arrays and placement new, I would use std::vector. Container classes are better for a number of reasons:
[www.parashift.com]

Assuming ftImage's copy constructor is written correctly (deep copy), you could do the following:
#include < vector >
using std::vector;

vector< ftImage > levelNumTxt(12, ftImage(640,480));

This is almost as efficient as an array with placement new (only one extra temporary is created -- the one passed into the vector constructor). The vector is initialized using placement new and ftImage's copy constructor, and you have the added benefit that std::vector automatically destroys its objects and frees its memory.



Edited 2 time(s). Last edit at 11/08/2010 08:39PM by calvinss4.
Re: declaring an array problem
November 09, 2010 06:46AM
Thanks for that help, but the problem with my code still exists. When I call:
levelNumTxt.setFont(hyperspd_ttf, hyperspd_ttf_size);
the code still doesn't run on my wii, and dolphin gives me an error.
Here is the whole code I have for this part:
std::vector< ftImage > levelNumTxt(12, ftImage(640,480));

for(i=0;i<totalLevels||i<12;i++){
		levelNumTxt.setFont(hyperspd_ttf, hyperspd_ttf_size); <- If this line is commented out the code runs
		levelNumTxt.setSize(50);
		levelNumTxt.setColor(Color::Color(0,0,0));
		levelNumTxtSpr.SetPosition(100+floor(i/3)*114,100+(i%3)*133);
		levelNumTxtSpr.SetImage(&levelNumTxt);
		sprintf(temp,"%d",i);
		levelNumTxt.printf(temp);
		manager.Append(&levelNumTxtSpr);
	}
here is the exact error that dolphin gives:
ERROR: Trying to compile at 0. LR=80008b04
I hit OK and:
iCacheJIT: Reading Opcode from fffffffc. Please report.
When I hit ok then dolphin crashes. When the code is run on the wii, nothing is displayed after a while, so I assume that there is an error.
Re: declaring an array problem
November 09, 2010 09:07AM
Do you have access to the ftImage class? If you do, post the copy constructor; I bet it does a shallow copy. If you can't modify the class then you may have no choice but to use placement new.
Re: declaring an array problem
November 09, 2010 12:34PM
(Just a small note from me: This is why I tell people to stay away from C++, to understand all these things you need a very good understanding of what is happening. std::vector makes heavy use of copy constructors and assign operators for example)

ftImage **levelNumTxt = (ftImage**) malloc(12*sizeof(ftImage*));
	for(i=0;i<20;i++){
		levelNumTxt = new ftImage(640,480);
                levelNumTxt->setColor(Color::Color(0,0,0));
	}

Try something like this, it avoids std::vector, copy constructors, and assign operators. In general it's easier to work with arrays of pointers then arrays of objects.
Re: declaring an array problem
November 09, 2010 08:37PM
You can solve anything with enough levels of indirection :)
Re: declaring an array problem
November 10, 2010 12:04AM
Well, I still have the problem. Here is the edited version of the code:
ftImage **levelNumTxt = (ftImage**) malloc(12*sizeof(ftImage));
	for(i=0;i = new ftImage(640,480);
 	}	
 
 for(i=0;i<totalLevels||i<12;i++){
 		levelNumTxt->setFont(hyperspd_ttf, hyperspd_ttf_size); <-This line still causes problems
		levelNumTxt->setSize(50);
		levelNumTxt->setColor(Color::Color(0,0,0));
		levelNumTxtSpr.SetPosition(100+floor(i/3)*114,100+(i%3)*133);
		levelNumTxtSpr.SetImage(levelNumTxt);
		sprintf(temp,"%d",i);
		levelNumTxt->printf(temp);
		manager.Append(&levelNumTxtSpr);
	}

I get the same results with that line in, except, now dolphin gives me about ten READ errors in consecuative addresses, and then there are about four WRITE errors. After this dolphin crashes.

ftImage header code:
#pragma once

#include "rgbaImage.h"

#include set
using std::set;

class ftImage : public rgbaImage {
public:

	class position {
	public:
		position(unsigned int x,unsigned int y);
		unsigned int x,y;
		
		bool operator <(const position &p2) const;
	};

	ftImage(const unsigned int width, const unsigned int height);
	~ftImage(void);

	void setSize(int s);
	void setColor(Color c);
	void setFont(const u8 *fontbuf, const u32 fontsize);
	void printf(const char *fmt, ...);
	void clear(const pixel &p);
	void clear();
	void reset();
	void operator~(void);
	void flush(void);

private:
	int _width, _height;
	//used by flood fill
	void tryToAdd(set &open,bool *closed,const pixel &p,signed int xOffset,signed int yOffset,const position &pos);
};

I don't have the source to this code, so I can't show it.
Re: declaring an array problem
November 10, 2010 05:39AM
You wrote:
ftImage **levelNumTxt = (ftImage**) malloc(12*sizeof(ftImage));

Unfortunately this compiles because malloc returns (void *) which is castable to (ftImage **), but you actually want to allocate space for 12 pointers:
ftImage **levelNumTxt = (ftImage**) malloc(12*sizeof(ftImage*));

Was there a copy-paste error after that? Looks like it. Make sure you actually construct the object in the while loop:
levelNumTxt = new ftImage(640,480);

If the number of levels in your game is a compile-time constant (usually the case) then you don't have to use malloc:
ftImage *levelNumTxt[12]; // array of the 12 pointers
Re: declaring an array problem
November 10, 2010 06:12AM
Ok, that didn't change anything. I changed it so that malloc allocates the correct number of pointers, and your right, it was a copy error, my bad, the revised code (just the first half), is here:
ftImage **levelNumTxt = (ftImage**) malloc(totalLevels*sizeof(ftImage*));
	for(i=0;i<totalLevels;i++){
		levelNumTxt = new ftImage(640,480);
	}
Re: declaring an array problem
November 10, 2010 09:22AM
We were operating under the assumption that
levelNumTxt.setFont(hyperspd_ttf, hyperspd_ttf_size);
stopped working AFTER you started using dynamic memory allocation. It appears we have to reevaluate that predicate.

Was this ever working in the first place? Does it work for one object?
ftImage test1(640,480);
test1.setFont(hyperspd_ttf, hyperspd_ttf_size);

Since you don't have access to the internals you are left with primitive trial and error debugging. If the above works, next see if it works with two objects:
ftImage test1(640,480);
test1.setFont(hyperspd_ttf, hyperspd_ttf_size);
ftImage test2(640,480);
test2.setFont(hyperspd_ttf, hyperspd_ttf_size);

As far as I can tell, ftImage::setFont takes a pointer to read-only memory (which makes sense for a font description), so you should be able to share it among any number of objects. If it works for one object but not multiple objects then there's something very strange going on here...
Re: declaring an array problem
November 10, 2010 03:19PM
Ok I did that and it worked. Two different ftImages worked fine. I even made one of them a pointer to a ftImage and it worked fine. I did get it to fail with the same problem when I defined an ftImage like this:
ftImage *levelNumTxt[5] = {new ftImage(640,480)};
What this should do is create an array of pointers to ftImages right? Then it should assign the first pointer to a new ftImage.
Re: declaring an array problem
November 10, 2010 08:04PM
Quote
g_man
What this should do is create an array of pointers to ftImages right? Then it should assign the first pointer to a new ftImage.

That is correct.

This is bad news bro, looks like we're dealing with heap corruption. This could be caused by your code somewhere else, or it could be caused by a library you're using, especially if that library uses threads (the exact same thing happened to me in my game using an array of GuiImageData).

At this point it's worth going over how heap memory works. 'malloc' and 'new' allocate from the heap.
- When you allocate an array of ftImage's on the heap, you are guaranteed to have a contiguous chunk of memory on the heap, so if writing to one ftImage corrupts heap memory before or after the ftImage you're writing to, it's highly likely to overwrite memory that's being used by a neighboring ftImage, then bam!
- If you allocate each individual ftImage on the heap, then the memory for each object doesn't have to be contiguous, so it's less likely that the memory overwritten is actually being used by your program (however, the compiler can still make the memory contiguous if it desires to, so no guarantees here).

Unfortunately, without a proper debugging environment and memory debugging software, debugging memory corruption is VERY VERY difficult; I hate to say it but you're better off resorting to hacks.

Based on your statement that it works for two statically allocated ftImage's, I'm going to assume that heap memory is corrupted, but statically allocated memory is fine. In that case try the following:
static const int totalLevels = 12;
char buffer[totalLevels * sizeof(ftImage)]; // statically allocate memory for 12 ftImage objects
ftImage * const levelNumTxt = (ftImage *)buffer; // convenience pointer, treat as array

int main()
{
  // initialize the array memory
  ftImage *pImg = levelNumTxt;
  for (int i = 0; i < totalLevels; ++i, ++pImg)
  {
    new (pImg) ftImage(640,480); // placement new
    pImg->setFont(hyperspd_ttf, hyperspd_ttf_size);
    ... // more initialization here
  }

  ...
  // random access
  levelNumTxt[2].setColor(Color::Color(0,0,0));
}

Heap corruption is no joke; godspeed, fellow programmer!
Re: declaring an array problem
November 11, 2010 12:23AM
I have good news and bad news

The good news:
Thanks, my code is running now!!!!!!!!!! :D :D :D :D

The bad news:
None of the numbers are showing up on the screen :(

I have removed anything that might be above them, but that doesn't make a difference. here is the code:
int totalLevels =5;
	int i;
	char temp[5];
	char buffer[totalLevels * sizeof(ftImage)];
	ftImage *const levelNumTxt = (ftImage *)buffer;
		Sprite levelNumTxtSpr[12];
	
	ftImage *pImg = levelNumTxt;
	for(i=0;i < totalLevels;i++,pImg++){
		new (pImg) ftImage(640,480);
		pImg->setFont(hyperspd_ttf,hyperspd_ttf_size);
		pImg->setSize(50);
		pImg->setColor(Color::Color(0,0,0));
		sprintf(temp,"%d",i);
		pImg->printf("1");
		levelNumTxtSpr.SetPosition(100+floor(i/3)*114,100+(i%3)*133);
		levelNumTxtSpr.SetImage(&levelNumTxt);
		manager.Append(&levelNumTxtSpr);
		
	}	

while(1){		
		for(i=0;i < totalLevels&&i < 12;i++){
			levelNumTxt.printf("1");
			levelNumTxt.flush();
		}	
	//	levelNumTextSpr[0].Draw();

		manager.Draw(0,0);
		
		for(i=0;i < totalLevels&&i < 12;i++){
			levelNumTxt.clear();
			levelNumTxt.reset();
		}

		gwd->Flush();
	}




Edited 2 time(s). Last edit at 11/11/2010 07:02PM by g_man.
Re: declaring an array problem
November 11, 2010 03:56AM
Edit your post and put spaces around the < signs so that the wiki can display your code properly.
Sorry, only registered users may post in this forum.

Click here to login