When good code goes bad
Reason #26972593-1 why I hate C++ development: Just fixed up some broken logic pertaining to binding names in the language service. All tests pass when running debug, but everything fails when running free. Normally when this happens it's due to me not initializing a variable (what a great feature that is!) and a dummy value was picked that ended up working in debug. For example you have something like:
if (foo == bar) {
}
And you haven't initilized foo or bar so they both got a dummy value of something like 0xecececec. So the test works in debug (which follows what you expected because you thought both values would be 0. However, in retail the values are totally gibberish and it's broken. Determing what the actual problem is a exercise in unhappiness. Stepping through retail code is possible, but it's not fun. My personal favorite is when you have code like:
if (FAILED(hr = DoSomething())) {
return hr;
}
When you step over the call to DoSomething you'll land on the “return hr;” line making you think that that call failed. You then step back (or re-run) and walk through the DoSomething method to find out what failed. It actually didn't fail, but with all the compiler otpimizations in place what you're probably actually seeing is the current location moving to jsut after the return and the IDE just picking an unfortunate location to say where you are. So you've wasted 5 minutes trying to walk through that code only to find out that there was nothing wrong with it. Sigh...
Oh well, some day I'll be off this bloody language :-)
Edit: I traced through it and it did indeed turn out to be an unintialized class field. Why isn't there a warning a can turn on to let me know about this?? Is there a warning that I just don't know about?? I don't think that there are words to express how much I love this language.
Comments
- Anonymous
June 20, 2004
C# might become a better language when Graphics class becomes as good as Win32 API (no, DllImport will not do) and I will not have to call Dispose on every pen/brush/whatever which I don't have to do in C++ (surprise). Unloading asemblies would be another useful addition. - Anonymous
June 20, 2004
Mikhail: The using statement should call dipose for you and will allow these unmanaged resources to get cleaned up appropriately.
I believe that unloading assemblies is something that is possible to do (at least in Whidbey).
You ask for the graphics class to be as good as Win32. But isn't that exactly what DllImport gets you? Direct access to the Win32 api?
Note: these issues are more with the provided libraries, not with the C# language itself. I think you're asking for more capable libraries that fit better with the .Net model rather than interop libraries to the current unmanaged APIs.
You may want to look into avalon to see if you like that model better. - Anonymous
June 20, 2004
Nope, you can't unload assemblies, see http://weblogs.asp.net/jasonz/archive/2004/05/31/145105.aspx. You can reset appdomain if you are lucky to run your code in a separate appdomain.
I presonally prefer deterministic destruction as one available in MC++, AFAIK.
As for Win32 API, why would I be writing in managed language then? If you look how ugly managed code looks when it relies on Win32 API call 99% of the time, you'd prefer native :-) - Anonymous
June 20, 2004
MIkhail: Doesn't dispose given you your deterministic destruction? When you dispose, the resources are freed back to the system.
Can you give me an example of how ugly it looks? Normally when I've p/invoked i've just passed structs and strings back and forth and it was fine. - Anonymous
June 20, 2004
The comment has been removed - Anonymous
June 20, 2004
I find the "using" syntax clumsy when working with multiple resources, and would prefer something like this:
{
dispose Brush b;
dispose Brush c;
...
}
which would translate into:
try
{
Brush b;
Brush c;
...
}
finally
{
if (c != null) c.Dispose();
if (b != null) b.Dispose();
} - Anonymous
June 20, 2004
Interesting: I guess I've never minded:
using (Brush b = ...)
using (Brush c = ...)
{
}
it's less characters than the dispose keyword :-) - Anonymous
June 20, 2004
The comment has been removed - Anonymous
June 20, 2004
Methinks you need to try something like Valgrind ( http://valgrind.kde.org/ )
Yeah, okay, it's a GPL open source project associated with KDE and it's only currently supported on Linux, but I'm being serious.
The idea is that it runs an emulation of the X86 processor - much slower than reality, of course - but it [1] tracks each and every single bit (yes - bit level) of memory that a program uses and alerts you to any use of uninitialized values.
([1] Well, this is the traditional 'memcheck' skin, at any rate http://developer.kde.org/~sewardj/docs-2.0.0/mc_main.html - other skins can check other things.) - Anonymous
June 20, 2004
Shannon: I'm aware of the grinned :-)
However, it has requirements that aren't going to help here, namely glibc.
I'm not looking for something that will catch the bugs at runtime, I'd rather have a compile time check that would at least warn you - Anonymous
June 20, 2004
Never thougt about your
using (Brush b = ...)
using (Brush c = ...)
{
}
I've always thought I have to write
using (Brush b = x)
{
using (Brush c = y)
{
}
} - Anonymous
June 20, 2004
Does that alleviate the need for the new syntax you wanted? - Anonymous
June 20, 2004
If you don't like using unintialized variables just turn on warnings in your C++ compiler. - Anonymous
June 20, 2004
Which warning tells you that your constructor has not initialized all the variables in the class? - Anonymous
June 20, 2004
Have a look pc-lint (http://www.gimpel.com/). It checks for uninitialised variables and out of order member assignments to mention just a few.
Regards
Lee - Anonymous
June 21, 2004
You said you didn't intialize a variable, not a class field. There's a warning for variables but not for class fields. - Anonymous
June 21, 2004
The comment has been removed - Anonymous
June 22, 2004
the
using (...)
using (...)
{
}
construct is very nice... never thought about that :)