tags:

views:

507

answers:

8

A wide range of structures is used in Win32 programming. Many times only some of their fields are used and all the other fields are set to zero. For example:

STARTUPINFO startupInfo; // has more than 10 member variables
ZeroMemory( &startupInfo, sizeof( startupInfo ) ); //zero out
startupInfo.cb = sizeof( startupInfo ); //setting size is required according to MSDN
startupInfo.dwFlags = STARTF_FORCEOFFFEEDBACK;
//Now call CreateProcess() passing the startupInfo into it

I want to stop copy-pasting such code and instead use an abstraction that would care about zeroing and setting parameters. Let's presume I only need the struct initialized as in example, and no other tuning is ever needed. Is the following a good solution? What are possible problems?

class CStartupInfo : public STARTUPINFO {
public:
   CStartupInfo()
   {
       ZeroMemory( this, sizeof( STARTUPINFO ) );
       cb = sizeof( STARTUPINFO );
       dwFlags = STARTF_FORCEOFFFEEDBACK;
   }
};

I'm in particular concerned about the ZeroMemory() call - looks like I fully control the code and the class has no vtable and calling ZeroMemory() this way is safe and there's no big difference between the two code snippets except that the latter provides an abstraction. Are there any caveats?

+5  A: 

Instead of subclassing, why not create a function instead?

STARTUPINFO CreateStartupInfo( DWORD flags ) {
  STARTUPINFO info;
  ZeroMemory(&info, sizeof(info));
  info.cb = sizeof(STARTUPINFO);
  info.dwFlags = flags;
  return info;
}

True this can put a non-trivial sized struct on the stack. If the compiler in question does Named Return Value Optimization (link) there will only be one copy created. But in any case, your example you already had once instance and temporarily putting a second is unlikely to cause major problems.

I usually only subclass a struct if there is a resource that is not properly managed in the struct. Typically it's to implement an RAII model. In your particular example no extra resource management is occurring so I would avoid the subclass and just use a function.

JaredPar
Good solution. Why? Because the compiler will optimize it away completely through NRVO! *No second copy* of the struct will be created on the stack, no copying of the return value takes place (if the above is used in an initialization, that is). The function simply and purely has zero overhead.
Konrad Rudolph
@Konrad, ah yes I forgot about NRVO: http://msdn.microsoft.com/en-us/library/ms364057.aspx
JaredPar
A: 

You can create special wrappers of types that behave like ordinary types, but initialized with zeros, like:

class zbool {
private:
  bool value;
public:
  zbool(const bool value) { ... }
  operator bool() { ... }
  // ... code skipped
};

And then use such types:

struct MyStruct {
  zbool deleted;
};

Of course this won't work if you trying to initialize external structures.

stepancheg
-1 You can't derive from built-in types in C++.
James Hopkin
Yes, I can't. Sorry, I haven't written in C++ for years, but you've got the idea.
stepancheg
+4  A: 

For structures you can do:

STARTUPINFO startup_info = { sizeof(STARTUPINFO), 0 };
startup_info.dwFlags = STARTF_FORCEOFFFEEDBACK;

which I find is a neat trick to initialise these sort of structures. However the catch is that the cb (or size/length) field must be the first one in the structure. You could also just do the extended version if need be:

STARTUPINFO startup_info = { 0 };
startup_info.cb = sizeof(STARTUPINFO);
startup_info.dwFlags = STARTF_FORCEOFFFEEDBACK;

If you want to wrap the structures with a class I'd recommend you try ATL/WTL first since the structures you're wrapping might already exist as classes there.

If you were still keen on creating your own class I would suggest that you create a constructor that takes every element of the structure in order, with default parameters specified, so that it's easier to change those values later on.

Daemin
In C++ you don't even need the 0.
dalle
Small point: you don't need the zeroes. STARTUPINFO startup_info = { sizeof(STARTUPINFO) }; is sufficient (the rest of the struct is value initialised)
James Hopkin
Are you sure? Following the philosophy of not paying the cost if you don't want to I'd assume that by default uninitialised structures would have garbage in them.Regardless I would put the zero in to be explicit.
Daemin
+2  A: 

You can use a template:

template <class T>
class selfzero : public T
{
public:
    selfzero() {
     ZeroMemory( this, sizeof( selfzero<T> ));
    };
};

and then:

{
 selfzero<STARTUPINFO> si;
}

The caveat: use this on a class or struct that has a vtable or got a vtable later, and it will go bang.

TonJ
+3  A: 

I've used tonj's suggestion, but since it kills intellisense to often, I ended up prefering this:

template <typename T>
T& ZeroInit(T & data) 
{ 
  ZeroMemory(&data, sizeof(data)); 
  return data;
}

template <typename T>
T& ZeroInitCB(T & data) 
{ 
  ZeroMemory(&data, sizeof(data)); 
  data.cb = sizeof(data); 
  return data;
}

Compared to selfzero<> It's another line in the normal case:

STARTUPINFO si;
ZeroInitCB(si);

But - as said - I opted for helping intellisense ;)

The return T& sometimes allows chaining, but I am not using it that often.

peterchen
+1  A: 

I think this is a fine way to make such structures more bulletproof. I'm not sure why others seem to not like the technique. I use it occasionally, but not as often as I otherwise might because it doesn't seem to be very well liked by coworkers for some reason.

I don't see it used in published material very often - the only one I could find in a quick Google right now is an article by Paul DiLascia in MSJ August 1997 (http://www.microsoft.com/MSJ/0897/C0897.aspx):

CRebarInfo and CRebarBandInfo are programmer-friendly C++ versions of the C structs REBARINFO and REBARBANDINFO, with constructors that initialize the objects to all zeroes before setting the cbSize member appropriately.

I can't think of much in the way of drawbacks (except the lack of acceptance). If anyone else can point to something more concrete, I'd appreciate it.

Michael Burr
I may be mistaken but those classes seem to be part of MFC? If so that's a heck of a large framework to use for this simple convenience. (That and I'm an ATL/WTL man myself.)
Daemin
The structs DiLascia wraps are part of the Windows SDK if I'm not mistaken, but the technique can be used on any POD struct - it's not tied to MFC, ATL, or Windows. The DiLascia article is about working with some MFC widget - it's not about wrapping the structs in a class that inits them. Wrapping the structs is just a techinque he uses and mentions in passing in the article.
Michael Burr
+1  A: 

To improve on TonJ's solution:

template <class T>
class selfzero : public T
{
public:
    selfzero() {
        ZeroMemory( (T*) this, sizeof( T ));
    };
};

Zeroes T, not selfdata. Even safe in cases of multiple inheritance, vtables and the likes. The T structure must be allocated contiguously in memory, and ((T*) this) is properly adjusted for other base classes and vtables.

MSalters
A: 

if all the nonstatic data members of the struct have trival constructors your class has a trivial constructor that is going to intialize members to their default values. Most of the time the exact same as zeroing the struct on purpose. So while might be "good" practice to zero them for initialization, most of the time there won't be any need.

piotr
Built-in types are not initialized by default unless an initializer is explicitly included into the user-define constructor initializer list. An the structs from WinAPI headers have no user-defined constructors.
sharptooth
Try it yourself:main.cc<pre><code>#include <iostream>#include <string>#include <cstdlib>#include <cassert>#include <vector>#include <stdexcept>using namespace std;class P {public: P() { cout << "P()" << endl; }};struct T { P p; int a; int b;};int main(int argc, char *argv[]) { T t;}</code></pre>It outputs "P()"
piotr