Tetris Problems

Started by
18 comments, last by DarkAnt 15 years, 8 months ago
Quote:Original post by DarkAnt
I've been at this for a little while, but I can't figure out how to make the shared pointer use the custom deleter. I've found all sorts of neat tricks that use the custom deleter, but I haven't found any examples that plainly explain how to implement a simple custom deleter.
Did you try what I posted earlier? That is:
d_tile(0, &SDL_FreeSurface)
If so, did it not work? And if not, what did you try? Can you post some code?
Advertisement
Well I've tried a few things:
class Tile{  typedef boost::shared_ptr<SDL_Surface>  surface_ptr; public:  Tile(int y_coord, int x_coord,       int x_img_loc, int y_img_loc,       int width = 32, int height = 32);  Tile(){d_id = getNewID(); d_tile(0,&SDL_FreeSurface);}  ~Tile();  ... private:  ...  surface_ptr d_tile;};

That gave me this error message
error C2064: term does not evaluate to a function taking 2 arguments

After reading some documentation that I only partially understood I found that the template had this format
template<class Y, class D> shared_ptr(Y * p, D d);

"Requirements: p must be convertible to T *. D must be CopyConstructible. The copy constructor and destructor of D must not throw. The expression d(p) must be well-formed, must not invoke undefined behavior, and must not throw exceptions.-from Boost's documentation on shared pointers"

From what I gathered 'd' is a functor. So I added this
typedef struct  {    void operator()(SDL_Surface* surface) const	{		cout << "deleting surface" << endl;		if(surface)			SDL_FreeSurface(surface);	}  } surface_destructor;

and changed the initializer to
Tile(){d_id = getNewID(); d_tile = surface_ptr(0,surface_destructor());}

I got the same error message so I googled some more and ran across this site http://learningcppisfun.blogspot.com/2007/05/custom-deleters-with-smart-pointers.html

So instead of trying to understand what was going on(brilliant of me I know...) I just tried to copy his pattern and ended up with this
typedef boost::shared_ptr<SDL_Surface,surface_destructor>  surface_ptr(0,surface_destructor());...Tile(){d_id = getNewID(); d_tile.release();}

And got this error message(well, along with his 147 friends)
error C2977: 'boost::shared_ptr' : too many template arguments

You know I'm starting to think I don't have a clue as to what I'm doing :P
Ok, let's back up to this:
class Tile{  typedef boost::shared_ptr<SDL_Surface>  surface_ptr; public:  Tile(int y_coord, int x_coord,       int x_img_loc, int y_img_loc,       int width = 32, int height = 32);  Tile(){d_id = getNewID(); d_tile(0,&SDL_FreeSurface);}  ~Tile();  ... private:  ...  surface_ptr d_tile;};
You'll note that in my earlier post I stated that the specified code went 'in the initializer list'. If you're not sure what an initializer list is, Google will tell you, but meanwhile here's an example:
class Tile{  typedef boost::shared_ptr<SDL_Surface>  surface_ptr; public:  Tile(int y_coord, int x_coord,       int x_img_loc, int y_img_loc,       int width = 32, int height = 32);  Tile() : d_tile(0,&SDL_FreeSurface)      {d_id = getNewID();}  ~Tile();  ... private:  ...  surface_ptr d_tile;};
Now, I've never used shared_ptr in exactly this way, so I may have something wrong. I would give the above a try though (I'm pretty sure it's right), and then post back if you have any more problems.
Sorry, I did try putting it in Tile's initializor list initially, but it didn't work. I got this error:
error C2661: 'boost::shared_ptr<T>::shared_ptr' : no overloaded function takes 2 arguments

I don't understand why it would have to be in the class initializer. Couldn't I have a shared pointer point to an array of structs and have it use a custom deleter? I wouldn't have an initializor list to use if that were the case.
Quote:Sorry, I did try putting it in Tile's initializor list initially, but it didn't work. I got this error:
error C2661: 'boost::shared_ptr<T>::shared_ptr' : no overloaded function takes 2 arguments
Can you post the code? (That is, the version of your code where you initialize your shared pointer object via the initializer list.)
Quote:I don't understand why it would have to be in the class initializer. Couldn't I have a shared pointer point to an array of structs and have it use a custom deleter? I wouldn't have an initializor list to use if that were the case.
You wouldn't create a shared pointer pointing to an array of SDL_Surface objects, but rather an array (or other container) of shared pointers, e.g.:
typedef boost::shared_ptr<SDL_Surface> surface_ptr_t;typedef std::vector<surface_ptr_t> surface_ptrs_t;surface_ptrs_t surfaces;
Anyway, don't give up! You're on the right track. (It would help though if you could post your code in its current form so we can see exactly what you're doing.)
Quote:Original post by jyk
Can you post the code? (That is, the version of your code where you initialize your shared pointer object via the initializer list.)
Quote:I don't understand why it would have to be in the class initializer. Couldn't I have a shared pointer point to an array of structs and have it use a custom deleter? I wouldn't have an initializor list to use if that were the case.
You wouldn't create a shared pointer pointing to an array of SDL_Surface objects, but rather an array (or other container) of shared pointers, e.g.:
typedef boost::shared_ptr<SDL_Surface> surface_ptr_t;typedef std::vector<surface_ptr_t> surface_ptrs_t;surface_ptrs_t surfaces;


What I was trying to say was that if I used a shared pointer outside of a class and I still wanted to use a custom deleter then how would I do that. I wouldn't have a class initializer to use.

Quote:Anyway, don't give up! You're on the right track. (It would help though if you could post your code in its current form so we can see exactly what you're doing.)

Haha, I've spent way too much time on this problem for me to give up. Thanks again for continuing to help me out.

As for the code
class Tile{  typedef boost::shared_ptr<SDL_Surface>  surface_ptr; public:  Tile(int y_coord, int x_coord,       int x_img_loc, int y_img_loc,       int width = 32, int height = 32);  Tile() : d_tile(0,&SDL_FreeSurface){d_id = getNewID();}  ... private:  ...  surface_ptr d_tile;};
you could try cleaning it up a little this way...

for(int x1 = 0; x1 < d_tile->w; x1++)				for(int y1 = 0; y1 < d_tile->h; y1++){					x2 = y1;					y2 = d_tile->h-1-x1;switch(bytes_per_pixel){		case 1: // 8 bpp			putPixel8(d_tile, x2, y2, &pixels_copy[(y1*d_tile->pitch)+x1*3]);			break;		case 2: // 16 bpp					putPixel16(d_tile, x2, y2, (Uint16 *)pixels_copy[(y1*d_tile->pitch/2)+x1]);			break;		case 3: // 24 bpp					putPixel24(d_tile, x2, y2, &pixels_copy[(y1*d_tile->pitch)+x1*3]);			break;		case 4: // 32 bpp					putPixel32(d_tile, x2, y2, (Uint32 *)pixels_copy[(y1*d_tile->pitch)+x1]);			break;	}}
Quote:Original post by CryoVenom
you could try cleaning it up a little this way...

Yeah, that looks much better, but I end up going through the switch statement d_tile->w*d_tile->h times instead of just once. I guess it really doesn't matter for the case of a Tetris game. I think a better way might be to just use function pointers.
int (Tile::*putPixel)(SDL_Surface* s, int x, int y, UintX* some_pixel)

The UnitX is where I'd have problems. I guess I could make all of the putPixel functions take a Uint8* and then grab the appropriate amount of bytes based on that offset. Or maybe I could put that function pointer into a template.
Ok, I tried the 'd_tile(0,&SDL_FreeSurface)' syntax, and it didn't work. Sorry about that :-|

I've never used the 'custom deleter' feature of shared_ptr in exactly this way, so I had to take a look at the Boost docs. It looks like what you really want to do is leave the shared pointer out of the initializer list altogether (its default initializer will still be invoked), and then submit your deleter whenever you reset the shared pointer, like this:
surface.reset(SDL_CreateRGBSurface(...), &SDL_FreeSurface);
A pitfall of using shared pointers and custom deleters in this way is that if a shared pointer 'lives' past the point where SDL is shut down, invoking the deleter function (SDL_FreeSurface() in this case) may cause problems.

This really shouldn't be a problem unless you have global or static shared pointer objects (inadvisable), but just in case, you might wrap SDL_FreeSurface() in a function that first checks to make sure SDL is still active, and throws or asserts if it's not.
That worked quite nicely. Btw, you were right about not using memcpy. It caused me a bit of trouble. Anyway, thanks again!

This topic is closed to new replies.

Advertisement