Sign in to follow this  
Talonius

Proper C++ way to allocate a LPRGNDATA struct

Recommended Posts

Talonius    643
I'm learning from Andre Lamothe's books. While I enjoy his method of explanation, and mostly follow it, I detest his style of coding. (What I mean by that is the lack of C++ classes and methodology. He uses some portions of C++ but the portions used seemed to center on the conveniences that C++ provides rather than the structure. His books are books meant to teach DirectX though, not proper C++ coding. :)) When initializing a region data structure, I'm not sure how to do this "properly":
LPRGNDATA RegionData;
RegionData = (LPRGNDATA) malloc(sizeof(RGNDATAHEADER) + (p_RectangleCount * sizeof(RECT)));
memcpy(RegionData->Buffer, p_ClipList, (sizeof(RECT) * p_RectangleCount));

// Set up the header and member fields.
RegionData->rdh.dwSize          = sizeof(RGNDATAHEADER);
RegionData->rdh.iType           = RDH_RECTANGLES;
RegionData->rdh.nCount          = p_RectangleCount;	
RegionData->rdh.nRgnSize        = p_RectangleCount * sizeof(RECT);
	
RegionData->rdh.rcBound.left    =  64000;
RegionData->rdh.rcBound.top     =  64000;
RegionData->rdh.rcBound.right   = -64000;
RegionData->rdh.rcBound.bottom  = -64000;

return RegionData;


Would the best way to do this be to create a new LPRGNDATA struct and initialize RegionData->Buffer to a RECT array as needed? I just want to avoid malloc and free usage if I can. Good habits start early and all that. :) Thanks in advance.

Share this post


Link to post
Share on other sites
quasar3d    814
I don't really think there is a clean way to do this in c++, unless you create a new struct with an array with a fixed number rects. If you want to have a dynamic number of rects, this is probably the best way to do it.
Normally you wouldn't do something like this this way altogether in c++, but the win32 api wants it this way, so I guess this is about the only option.

In a clean c++ way it would be something like this:


class
{
Header header;

Rect *rects;
or
std::vector<Rect> rects;

};





but that won't result in the same data, so you won't be able to use that with the win32 api.

If you are going to use those structs a lot, you could create a wrapper class, but otherwise I would just do it on andre lamothes way.

If you just want to avoid malloc you can of course use new with chars, and then cast it to a LPRGNDATA, but that oviously defeats the purpose of new:

RegionData = (LPRGNDATA)new char[size];

Share this post


Link to post
Share on other sites
snk_kid    1312
You'll probably be better off sticking with using malloc on this one because win32 api was written in C and the definition of RGNDATA is:


typedef struct _RGNDATA {
RGNDATAHEADER rdh;
char Buffer[1];
} RGNDATA, *PRGNDATA;


as you can see Buffer is really gonna be more than one element, a C trick, malloc & free deal with uninitialized memory where as "normal" operator new allocates memory then initializes it by invoking constructors (even for built-in primitive types) so it would be pretty use-less in this context especially because of Buffer member, the closet you can get to that is probably:


RegionData = static_cast<LPRGNDATA>(::operator new(sizeof(RGNDATAHEADER) + (p_RectangleCount * sizeof(RECT)));

memcpy(RegionData->Buffer, p_ClipList, (sizeof(RECT) * p_RectangleCount));

/* ... */

::operator delete(RegionData);


which isn't any better just does exactly the same thing.

Share this post


Link to post
Share on other sites
hplus0603    11356
You CAN do this in C++, but it's somewhat crufty. You can use operator new, and placement new, together.


template< class Iter >
RGNDATA * NewRegion( Iter begin, Iter end )
{
size_t size = sizeof(RGNDATAHEADER)+sizeof(RECT)*(end-begin);
RGNDATA * ret = (RGNDATA *)::operator new( size );
new( ret ) RGNDATA; // this isn't really necessary
RECT * rct = reinterpret_cast< RECT * >( ret+1 );
for( size_t i = 0; i < rgn->nCount; ++i ) {
new( &rct[i] ) RECT; // this isn't really necessary
}
std::copy( begin, end, rct );
// fill out the region header here
...
return ret;
}

void DeleteRegion( RGNDATA * rgn )
{
RECT * rct = reinterpret_cast< RECT * >( rgn+1 );
for( size_t i = 0; i < rgn->nCount; ++i ) {
rct->~RECT(); // this isn't really necessary
}
rgn->~RGNDATA(); // this isn't really necessary
::operator delete( rgn );
}


If you want a class that looks more like a value class, I can't see how you could do this, and support variable size, without getting two allocations; one for the wrapper value class, and one for the actual data layout (which could just re-use the above code).

In that case, I'd probably also use ref counting of the region data; allocate an int more data than you need, and use that int for refcount before the actual region data. Offset the pointer by sizeof(int) when you need to get an actual region pointer.

The core problem is that the region data structure is laid out to minimize heap operations (which are expensive) rather than being convenient for high-level C++.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
If you just want to clip the display use the SetHwnd() method of IDirectDrawClipper. a lot cleaner than filling out a RGNDATA struct.

Share this post


Link to post
Share on other sites
JohnBolton    1372
The basic difference between malloc/free and new/delete is that the constructor and destructor are called. If the RGNDATA struct has no constructor or destructor then malloc/free and new/delete do approximately the same thing.

You could derive your own RegionData class from RGNDATA just to add a constructor and destructor, but that would probably be overkill.

Either way, if I have the choice, I always use new/delete over malloc/free because it is safer and it is extendable.

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