g++ 3.4.5 doesn't zero-initialize

A

Alf P. Steinbach

Grr.

What's wrong with this code?

struct WndClassEx: WNDCLASSEX
{
WndClassEx(
cppx::WideString const& name,
Params const& params
)
: WNDCLASSEX()
{
cbSize = sizeof( WNDCLASSEX );
style = params.style();
lpfnWndProc = &DefWindowProc;
hInstance = params.module();
hCursor = ::LoadCursor( 0, IDC_ARROW );
hbrBackground = reinterpret_cast<HBRUSH>( COLOR_BTNFACE + 1 );
lpszClassName = name.cStr();
}

WndClassEx const* ptr() const
{
return this;
}
};

Answer: nothing in particular, at least when one knows that it's just a little
local helper class where 'name' arg has lifetime guaranteed to be enough.

But MinGW g++ doesn't zero-initialize the WNDCLASSEX as it should (this is a
plain struct with no constructor, provided by the Windows API).

Argh!

I knew old MSVC doesn't always zero-initialize /arrays/ when told to.

But I didn't know that g++ doesn't zero-initialize /structs/ when told to.

And by the way, the gdb debugger is driving me crazy. Ignoring breakpoints and
telling me the source code for some stack frame is at arbitrary location in
arbitrary file. And yes, optimizations turned off, which is another annoyance,
since g++ needs optimizations on to e.g. warn about uninitialized things.

Not to mention the Code::Blocks IDE's debugger output display, which doesn't
display anything of interest, just a bunch of info lines about internal details
of the debugger, and actively *swallows* trace output, which is what I'd need.

Happily it's possible to view a (nearly, almost, but of course not 100%)
complete log of the IDE's interaction with the brain-damaged debugger beast, and
there one can present stderr output. But. Oh well.

I need coffee.


- Alf
 
B

Balog Pal

Alf P. Steinbach said:
Grr.

What's wrong with this code?

struct WndClassEx: WNDCLASSEX
{
WndClassEx(
cppx::WideString const& name,
Params const& params
)
: WNDCLASSEX()
{
cbSize = sizeof( WNDCLASSEX );
style = params.style();
lpfnWndProc = &DefWindowProc;
hInstance = params.module();
hCursor = ::LoadCursor( 0, IDC_ARROW );
hbrBackground = reinterpret_cast<HBRUSH>( COLOR_BTNFACE +
1 );
lpszClassName = name.cStr();
}

WndClassEx const* ptr() const
{
return this;
}
};

Answer: nothing in particular, at least when one knows that it's just a
little local helper class where 'name' arg has lifetime guaranteed to be
enough.

The problem is you build on working 'value-init' that is known to have
real-world problems.
But MinGW g++ doesn't zero-initialize the WNDCLASSEX as it should (this is
a plain struct with no constructor, provided by the Windows API).

Argh!

I knew old MSVC doesn't always zero-initialize /arrays/ when told to.

But I didn't know that g++ doesn't zero-initialize /structs/ when told to.

Does that version claim support for C++03? IIRC C++98 did not have the value
init for structs, and I guess the 3.x versions were about the turning point.
Even if it did claim TC1 I tend to doubt fair implementation in the first
year.

Honestly, the calendar is 2009 by now, but I'd not rely in value init even
of current compielrs -- at least without first checking the version and
include some defense cludges in code should it migrate away...
And by the way, the gdb debugger is driving me crazy.

Too bad -- if doing windows, why not use the windows toolset? I doubt gdb
will ever come close to Visual's... If your prog is portable, it will go in
VS express, if not -- what is the wisdom behind locking into a compiler
that is poorly supported on the target platform?
And if porting win code away, why to an archaic version of gcc instead of a
current?
Ignoring breakpoints and telling me the source code for some stack frame
is at arbitrary location in arbitrary file. And yes, optimizations turned
off, which is another annoyance, since g++ needs optimizations on to e.g.
warn about uninitialized things.

And inside ddd it crashes on all kind of ocasions, sometimes gets confused
of the objects to show, etc...
 
A

Alf P. Steinbach

* Balog Pal:
The problem is you build on working 'value-init' that is known to have
real-world problems.

So has everything. :)

I'd not know what to not avoid.

I think the statement is founded on blissful ignorance of how many constructs of
C++ that have real world problems with some popular compilers.

That includes exceptions, it includes RTTI such as dynamic_cast, even for-loops
(scope). It even includes standard 'main' (linking).

Avoiding for-loops, standard main, RTTI, default-initialization, exceptions...,
one would have, well, not C++!

Does that version claim support for C++03? IIRC C++98 did not have the value
init for structs, and I guess the 3.x versions were about the turning point.
Even if it did claim TC1 I tend to doubt fair implementation in the first
year.

The code does not depend on C++03 value initialization.

It uses only the subset of that functionality that was present in C++98, there
known as default-initialization.

C++98 had just zero-initialization and default initialization. For a POD, such
as in the code above, the latter is the *same* as value initialization, reducing
recursively to zero-initialization of the PODs elements. Andrew Koenig (IIRC)
introduced value initialization in C++03 to help address the inconsistent
behavior one got for non-PODs, that is, for aggregate structs, e.g. if you had a
std::string in there.

And since WNDCLASSEX is a POD structure, the above should work with a C++98
compiler.

Happily it turned out that it's only in the context of a memory initializer list
that g++ goofs up the default initialization, so the code's now


struct WndClassEx: WNDCLASSEX
{
WndClassEx(
cppx::WideString const& name,
Params const& params
)
//: WNDCLASSEX()
{
*static_cast<WNDCLASSEX*>( this ) = WNDCLASSEX(); // MinGW g++.

cbSize = sizeof( WNDCLASSEX );
style = params.style();
//cbClsExtra = 0;
//cbWndExtra = 0;
lpfnWndProc = &DefWindowProc;
hInstance = params.module();
//hIcon = 0;
hCursor = ::LoadCursor( 0, IDC_ARROW );
hbrBackground = reinterpret_cast<HBRUSH>( COLOR_BTNFACE + 1 );
lpszClassName = name.cStr();
//hIconSm = 0;
}

WndClassEx const* ptr() const
{
return this;
}
};


Honestly, the calendar is 2009 by now, but I'd not rely in value init even
of current compielrs -- at least without first checking the version and
include some defense cludges in code should it migrate away...


Too bad -- if doing windows, why not use the windows toolset? I doubt gdb
will ever come close to Visual's... If your prog is portable, it will go in
VS express, if not -- what is the wisdom behind locking into a compiler
that is poorly supported on the target platform?

On the contrary, to avoid a compiler lock-in one should compile the code with at
least 2 different compilers.

I use MingW for that purpose.

For the code that the above is part of it did spot two access issues and two
invalid extra semicolons! ;-)

However, I had to provide a number of declarations of Windows API things not
supported by the g++ Windows headers and import libraries.

Those were not, as one would expect, "new" things, but rather archaic things
where Microsoft has provided more complicated-to-use alternatives.

And if porting win code away, why to an archaic version of gcc instead of a
current?

g++ 3.4.5 *is*, unfortunately, the still current version of MinGW. :-(

There are of course "unofficial" Windows builds of later g++ versions since g++
is into version 4.x on *nix.

But I think it's best (at least for my purposes) to write code that will compile
with current versions of popupular compilers.


Cheers,

- Alf
 
M

Marcel Müller

Alf said:
Happily it turned out that it's only in the context of a memory
initializer list that g++ goofs up the default initialization, so the
code's now


struct WndClassEx: WNDCLASSEX
{
WndClassEx(
cppx::WideString const& name,
Params const& params
)
//: WNDCLASSEX()
{
*static_cast<WNDCLASSEX*>( this ) = WNDCLASSEX(); // MinGW
g++.

While your code may look more OO-like I would always prefer to use
memset for POD types.

memset(static_cast<WNDCLASSEX*>( this ), 0, sizeof(WNDCLASSEX));

does the job too and works most probably with any existing C++ compiler
that understands static_cast. And since memset is mostly an intrinsic
function it will usually not create any runtime overhead too.


:)
I successfully avoided gdb for at least the last 15 years. Fortunately
gcc can create debug infos that is readable by other debuggers too.


VS has the second place in the list of buggy or incomplete C++ compilers
in my experience. (The first place was Borland.)

On the contrary, to avoid a compiler lock-in one should compile the code
with at least 2 different compilers.
ACK.

However, I had to provide a number of declarations of Windows API things
not supported by the g++ Windows headers and import libraries.

I haven't tried on Windows, but gcc does not ship with a recent SDK. So
I usually use the platform specific SDK files with gcc.

g++ 3.4.5 *is*, unfortunately, the still current version of MinGW. :-(

cygwin with gcc4 package is at 4.3.2 as far as I know.
There are of course "unofficial" Windows builds of later g++ versions
since g++ is into version 4.x on *nix.

Whatever is an official build of a gcc on the windows platform?

But I think it's best (at least for my purposes) to write code that will
compile with current versions of popupular compilers.

Indeed, if someone else should compile your code you have no choice.

I tend to avoid uncommon C++ idioms in such cases unless I really need
them. Doing that I am able to compile some of my current code even on
the very old IBM compiler (Visual Age C++ 3.0). It takes about one tenth
of the time of gcc to compile a larger project.

The functionality of simple OO wrappers like your WndClassEx will almost
not depend on any C++ feature that is less the 15 years old.


Marcel
 
A

Alf P. Steinbach

* Marcel Müller:
While your code may look more OO-like I would always prefer to use
memset for POD types.

memset(static_cast<WNDCLASSEX*>( this ), 0, sizeof(WNDCLASSEX));

does the job too and works most probably with any existing C++ compiler
that understands static_cast. And since memset is mostly an intrinsic
function it will usually not create any runtime overhead too.

It's always fun when someone recommends memset. :)

"Hey, why do you use a simple assignment (or curly braces initialization, or
whatever) when you can use *memset*?"

:)


Cheers & hth.,

- Alf
 
A

Alf P. Steinbach

* Marcel Müller:
cygwin with gcc4 package is at 4.3.2 as far as I know.

CygWin isn't MinGW.

If one wants a later version of g++ for Windows than the MinGW one then there
are better alternatives than CygWin.

Whatever is an official build of a gcc on the windows platform?

The "official" MinGW build of g++ is the one supplied, or rather linked to, on
the MinGW pages.

CygWin is a big and ugly beast and you have to use combersome options to produce
an executable that doesn't depend on CygWin DLLs (or at least you had to, I
haven't followed the latest developments, if any, on the CygWin front).

Essentially simplyfying that is what MinGW does, or did. And other packages of
*nix tools for Windows address other aspects of CygWin. But MinGW is so far
behind in the versions that it's almost removed itself from the scene.


Cheers,

- Alf
 
M

Michael Doubez

* Marcel Müller:



CygWin isn't MinGW.

If one wants a later version of g++ for Windows than the MinGW one then there
are better alternatives than CygWin.



The "official" MinGW build of g++ is the one supplied, or rather linked to, on
the MinGW pages.

CygWin is a big and ugly beast and you have to use combersome options to produce
an executable that doesn't depend on CygWin DLLs (or at least you had to, I
haven't followed the latest developments, if any, on the CygWin front).

Essentially simplyfying that is what MinGW does, or did. And other packages of
*nix tools for Windows address other aspects of CygWin. But MinGW is so far
behind in the versions that it's almost removed itself from the scene.

There are custom MinGw release with recent gcc.

One is TDM:
http://www.tdragon.net/recentgcc/

Another is bundled with recent boost and other interesting libs (boost
among others):
http://nuwen.net/mingw.html

And IIRC, Mingw provides test release with more recent compilers (see
the sourceforge pages).
 
A

Alf P. Steinbach

* Michael Doubez:
There are custom MinGw release with recent gcc.

One is TDM:
http://www.tdragon.net/recentgcc/

Another is bundled with recent boost and other interesting libs (boost
among others):
http://nuwen.net/mingw.html

And IIRC, Mingw provides test release with more recent compilers (see
the sourceforge pages).

Yes, thanks, I listed those two URLs in some other article here yesterday.

Seems that there's some interest! :)


Cheers,

- Alf
 
A

Alf P. Steinbach

* Alf P. Steinbach:
* Marcel Müller:

It's always fun when someone recommends memset. :)

"Hey, why do you use a simple assignment (or curly braces
initialization, or whatever) when you can use *memset*?"

:)

Sorry, I should not just make fun of it. The serious arguments against 'memset',
when compared to curly braces initialization or simple assignment, are (1) it's
most often more to write, (2) it's less clear at a glance that it's correct, (3)
it can indeed more easily be incorrect, since there's no type checking, (4) it's
seldom more efficient and can easily be less efficient, and (5) like 'void main'
it indicates newbie, which is a misleading indication if one isn't.

Regarding efficiency, which is what newbies most often tend to use as argument
for it (here I'm not arguing against you but simply addressing a common newbie
objection to the above), all three routines in the code below generate

cld
mov ecx, 3
mov eax, 0
rep stosd

for clearing the Pod, when compiled with MinGW g++ 3.4.5 option '-O'.


<code>
#include <iostream>
#include <memory.h>

void show( int x ) { std::cout << x << std::endl; }

struct Pod
{
int a;
int b;
void* p;
};

void useInitializer()
{
Pod o = {};
show( o.a );
}

void useAssignment()
{
Pod o;
o = Pod();
show( o.a );
}

void useMemset()
{
Pod o;
memset( &o, 0, sizeof(o) );
show( o.a );
}

int main()
{
useInitializer();
useAssignment();
useMemset();
}
</code>


Cheers,

- Alf
 
A

Alf P. Steinbach

* Alf P. Steinbach:
* Alf P. Steinbach:

Sorry, I should not just make fun of it. The serious arguments against
'memset', when compared to curly braces initialization or simple
assignment, are (1) it's most often more to write, (2) it's less clear
at a glance that it's correct, (3) it can indeed more easily be
incorrect, since there's no type checking, (4) it's seldom more
efficient and can easily be less efficient, and (5) like 'void main' it
indicates newbie, which is a misleading indication if one isn't.

Regarding efficiency, which is what newbies most often tend to use as
argument for it (here I'm not arguing against you but simply addressing
a common newbie objection to the above), all three routines in the code
below generate

cld
mov ecx, 3
mov eax, 0
rep stosd

for clearing the Pod, when compiled with MinGW g++ 3.4.5 option '-O'.

Sorry -- the *assignment* in addition generates a following element-wise
assignment with that compiler.

It's evidently not a very smart optimizer.

But anyway the initializer and the memset generate identical code, and the
initializer is shorter, more clear, and guaranteed correct.
 
M

Michael Doubez

* Michael Doubez:








Yes, thanks, I listed those two URLs in some other article here yesterday..

I found them. Oups.
Seems that there's some interest! :)

I have been using the nuwen flavor (on my USB key as a quick toolchain
env) for over a year and I am quite happy with it. Note that it is
based on the TDM.
 
B

Balog Pal

"Alf P. Steinbach"
So has everything. :)

I'd not know what to not avoid.

I think the statement is founded on blissful ignorance of how many
constructs of C++ that have real world problems with some popular
compilers.

That includes exceptions, it includes RTTI such as dynamic_cast, even
for-loops (scope). It even includes standard 'main' (linking).

Avoiding for-loops, standard main, RTTI, default-initialization,
exceptions..., one would have, well, not C++!

You're building a strowman... The features you list are
- way more powerful to be attractive
- have no easy alteratives like explicitly writing a ctor for val-init
- IMO have different kind of problems that are not so hard to manage or work
around
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,059
Latest member
cryptoseoagencies

Latest Threads

Top