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
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...
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++!
>> 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.
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...
>
>> 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?
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
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.
>> 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.
:-)
I successfully avoided gdb for at least the last 15 years. Fortunately
gcc can create debug infos that is readable by other debuggers too.
>> 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?
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.
>> 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. :-(
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
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
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.
>> 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?
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
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).
--
Michael
Yes, thanks, I listed those two URLs in some other article here yesterday.
Seems that there's some interest! :-)
Cheers,
- Alf
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
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.
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.
--
Michael
> I need coffee.
>
> - Alf
Are you located near one of these?
Well worth a trip.
DoC
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