#define ZERO(ptr) memset(ptr,0,sizeof(*ptr)) BITMAPINFOHEADER bih; ZERO(&bih);However, this ZERO() macro is very easy to use in catastrophically bad ways. (and no I'm not talking about invalidating C++ objects or anything - I'm assuming that you are using it only on things that *can* be zeroed, you just accidentally use it wrong).
Exercise : what happens in this code ?
DWORD test1[4] = { 1,1,1,1 }; char * test2[4] = { "","","","" }; char * test3 = ""; ZERO(test1); ZERO(test2); ZERO(test3);(don't use a compiler to cheat).
And how do I write a better ZERO macro ? (I don't actually know the ideal answer to this question, so it's not entirely condescending rhetorical douchebaggery).
This comment has been removed by the author.
ReplyDeleteNot clean, but it seems to work fine: (replace [] by <> - your blog forbig "html tag" named typename ;) )
ReplyDeletetemplate [typename T]
void myZERO(T &ptr, size_t size)
{
printf( "Size=%d\n", size );
memset( &ptr,0,size );
}
template [typename T]
void myZERO(T *ptr, size_t size)
{
size = max( size, sizeof(ptr) );
printf( "SizePtr=%d\n", size );
memset( ptr,0,size );
}
#define ZERO(t) myZERO(t,sizeof(t))
int main()
{
int t[4];
char c;
short s;
int i;
ZERO(t);
ZERO(i);
ZERO(&i);
ZERO(s);
ZERO(c);
}
=======>
SizePtr=16
Size=4
SizePtr=4
Size=2
Size=1
Actually, it will fail for ZERO(&c) and ZERO(&s) :(
ReplyDeleteWhat happens in the sample code :
ReplyDeleteDWORD test1[4] = { 1,1,1,1 };
char * test2[4] = { "","","","" };
char * test3 = "";
ZERO(test1); // test1[0] = 0
ZERO(test2); // test2[0] = 0
ZERO(test3); // !! access violation! stomping const string
Just writing the macro slightly differently fixes the majority of problems :
ReplyDelete#define ZERO(obj) memset(&(obj),0,sizeof(obj))
is much better.
If you want something that's more restrictive you could do :
template typename T
void CheckedZero(T * ptr)
{
const T zero = { 0 }; // will fail compile if T has constructors
memset(ptr,0,sizeof(T));
}
#define CHECKED_ZERO(obj) CheckedZero(&(obj))
Hmm.. actually the simple ZERO(obj) thing on test3 (the char *) just sets the pointer to null, which is not really what you want.
ReplyDeleteI'd like that to be a compile error. I think Sly's way is good, but I would make the pointer version of myZERO just be a compile failure (and make one that catches arrays).
> char * test3 = "";
ReplyDelete> ZERO(test3); // !! access violation! stomping const string
That's because of a C/C++ weirdness: we're allowed to take non const pointers on const string char buffers. The crash will also occurs in this supposedly valid code:
char * test3 = "";
*test3 = 0;
Changing the test code to this will make it work:
char * test3 = strdup("");
Actually what one probably want to do instead is: ZERO(&test3) ;)
ReplyDeleteBut it does prove your point saying there's no perfectly safe ZERO stuff.
Yeah, ideally the cases like ZERO(test3) that are just totally ambiguous and obviously wrong I would like to just be a compile error.
ReplyDelete