Monday, January 19, 2009 

Visual C++ 2008 bug (C++/CLI)

I wrote some faulty code recently. Actually, the code was right, but the compiler didn't like it anyway. It took a while to discover it was a (known) compiler bug, so I hope I can save someone else's time by providing a link to relevant the Microsoft page: Static variable in native method causes exception c0020001 during process exit.

Shortly stated, if you use a local static variable (inside a function/method), and the type of that variable has a native/unmanaged destructor, the compiler gets confused and emits a managed destructor instead. Of course, that happens if your compilation unit is in mixed mode (native + managed).

Unfortunately, in my case the local static variable was inside a template, shared between native and mixed compilation units. In the end, we didn't adopt Microsoft suggestion of a separate unit (which kinda makes all the C++/CLI magic to disappear), but I just changed the type of the static to remove the destructor. That requires some tweaking, and I still hope we'll get a fix from Microsoft, although the page above reports a discouraging "Won't fix" decision...

Labels: , ,

Sunday, December 23, 2007 

Down to Earth

My recent posts have been leaning on the conceptual side, and I don't want to be mistaken for an Architecture Astronaut, so here is some down-to-earth experience from yesterday.

I've developed a few application for mobile phones over the years, mostly as J2ME (Java 2 Micro Edition) midlets, or targeting Windows Mobile phones.
I never ventured into developing a Symbian application in C++. However, for several months I've entertained some ideas, and they require to go down to the metal on a Nokia phone. Nokia provides only a C++ toolkit to access the data I need, so it was time for me to get back to good old C++, which I'm not using much lately.
I just needed some spare time to install all the necessary tools, which I somehow expected to be an unpleasant experience. It got worse :-).

It all started rather nicely. Nokia offers a Visual Studio 2005 plug-in (Carbide.vs 3.0), and they say it works on Windows Vista. Cool, as I have installed Vista in my new Core 2 Quad computer (ok, that might not have been a smart move :-).
So I download the thing, and I soon discover I have to download a bunch of other stuff as well (ActivePerl, a separate SDK, and whatever else). Well, at least I don't get any broken link during download, which is something :-).

I go ahead and install everything; well, I try to. Nokia seems to have tested Carbide.vs 3.0 on Vista, but not the other stuff. The installers fail, some with a nice error message (telling me to change the PATH manually), some silently, some (like the optional Microsoft PowerToys) with a cryptic error message. I discard the PowerToys, fix the PATH manually, and start up Visual Studio.

I get a dialog box requiring registration, fight a little with the slowest Nokia server on the planet, and I'm game. Thanks to the installed wizards, I get a barebone Symbian application in a few clicks. To my surprise :-), it builds and works fine in the emulator. So far, so good. I just need to build a release version and upload it into the phone.

Ok, there is no way to set a different target, just the emulator. Exploring the file system, I realise that the ARM toolchain has not been properly installed by the Symbian SDK installer. That is, the files have been created, but setup has not been started. I do it manually. I get a few problems with PATH again, and fix that manually too. Lucky for me, I kept all the default installation paths, although that means I've got files everywhere on the hard drive. I suspected they never tried to install anywhere else.

Well, now I can choose a release target in Visual Studio. When you try to build, however, you get a sequence of interesting errors. Fixing them was like entering hell's gates :-).

To make a long story very short, here are a few relevant links:

http://discussion.forum.nokia.com/forum/showthread.php?p=366765#post366765
http://discussion.forum.nokia.com/forum/showthread.php?t=107308
http://wiki.forum.nokia.com/index.php/Windows_Vista
http://wiki.forum.nokia.com/index.php/Moving_to_Windows_Vista#CSL_Arm_Toolchain

It's kinda fun to read that yeah, well, Carbide.vs 3.0 was tested on Vista, but the underlying SDK was not, and it does not work :-).

As usual, fixes you find in forums don't really apply verbatim, but in the end, after fixing paths, perl scripts, makefiles, and stuff like that, you can make it compile and even link. I had to copy as.exe like others to make it link too. I was naive enough to believe it was over.

Trying to upload the software to the phone, I got a certificate error. The phone was set up to accept unsigned applications (worked fine with unsigned midlets), but it refused the Symbian app anyway. Ok, time to sign the application. There is a time-limited developer certificate in the SDK, so I use that and get a signed app. Uploading fails again, with a "corrupted file" message from the phone.

Reading docs and dozens of internet posts did not help. The most frequent suggestion was to reset your phone, which I refused to do. Well, at least I realized there is an absurd signing policy (should I say police?) in Symbian, which requires developers to shell out some serious money if they intend to develop powerful apps. Quite dumb, but I won't talk about that, except to say it once again: DUMB :-).

In the end I tried to generate a brand new certificate, instead of using the one from the SDK. Guess what, it worked! (pure serendipity). Sure, I get all kind of warnings when installing on the phone (because it's a self-generated certificate) but the application is working. Now it's time to try out my little idea. Well, as soon as I get some time to waste, that is :-).

Bottom line: we can blame it on Vista; after all, even Microsoft PowerToys had problems. But if you look at the structure of Carbide.vs (and I had to :-), you'll see it's just a bunch of perl scripts creating makefiles on the fly, invoking gnu tools with crossed fingers, in the hope that somehow the whole stuff works. Gee. We had better development environments in the 80s :-).
I don't know if Carbide.c++ is any better than Carbide.vs in terms of structure. Of course, since the SDK is malfunctioning on Vista, Carbide.c++ will share the same problems as Carbide.vs. Maybe it's better structured. I honestly hope so :-), because as far as Carbide.vs goes, there is only one word to define it: unprofessional.

Labels: , ,

Saturday, July 14, 2007 

Get the ball rolling, part 2 (of 4, most likely)

I hope you had some time to read the XP episode paper and ponder a little on what they managed to get in the end. As I've anticipated, I do I have a few issues with the results. It's not a matter of correctness: they pass all the tests. It's a matter of quality.
The design is questionable, and even the code is questionable. No big deal: after all, it's a little more than a toy example. It gets slightly worse when you consider all this stuff in perspective (at the end of the XP game, code is all you get; I'll get back to this later). Right now, here are the main flaws I see:

- overabundance of numerical literals.
This is programming 101: literals are bad (although they keep your code a few line shorter). Go over the code. You'll see numbers like 10 just about everywhere. Unfortunately, in bowling you got 10 pins and also 10 frames. You always have to read carefully to understand which is which. Now go back to the wikipedia page on bowling. See the mention of 5-pin bowling, 9-pin bowling, etc? Just go ahead and change the code to use 9 pins and 10 frames(9-pin bowling ain't that simple, but anyway). Guess what, none of the test cases will help, and it's not a one-line change as it ought to be. There is even a 21 in that code, being 10 * 2 + 1. I'll let you figure what that 10, 2, and 1 are :-).

- programming against an implementation, not against the problem
Some portion of the code are indeed quite good:

 public int scoreForFrame(int theFrame)
  {
    ball = 0;
    int score=0;
    for (int currentFrame = 0; 
         currentFrame < theFrame; 
         currentFrame++)
    {
      if (strike())
        score += 10 + nextTwoBalls();
      else if (spare())
        score += 10 + nextBall();
      else
        score += twoBallsInFrame();
    }
    return score;
  }

You can basically read it in plain english. Unfortunately, most of the rest is not of the same quality, for instance:

 public void add(int pins)
  {
    itsScorer.addThrow(pins);
    adjustCurrentFrame(pins);
  }
  private void adjustCurrentFrame(int pins)
  {
    if (firstThrowInFrame == true)
    {
      if (adjustFrameForStrike(pins) == false)
        firstThrowInFrame = false;
    }
    else
    {
      firstThrowInFrame=true;
      advanceFrame();
    }
  }
...
  private void advanceFrame()
  {
    itsCurrentFrame = Math.min(10, itsCurrentFrame + 1);
  }

"Adjust" a frame? Couldn't we get it right on the first place :-)? Even a Math.min call?? C'mon. There isn't any resemblance with the problem domain. It's just programming against the implementation, until things seems to work. Now, I've seen a lot of code like this over the years; it's the typical code people get after years of tweaking, except here it didn't take years, just hours.
Now, tweaking code till it works is already bad per se, but it's much worse when you consider that in XP, at the end of the game, the code is the design, and the test cases are the requirements. Again, everything is fine when the domain is trivial, largely known, well documented elsewhere (like in this case). But if we tackle a difficult new problem in an uncharted domain, and all we leave behind is code like that, I pity the souls that will join the team.

- Feature Envy: Game over Frame
According to Fowler, a method (or worse, a class) is exhibiting Feature Envy
when it "seems more interested in a class other than the one it actually is in". A variation of Feature Envy is when the other class does not even exist, but it is continually talked about in a method (or another class). Take Game. Excluding empty lines. Game is 45 lines long; if you exclude brace-only lines, it's 25 lines long. Of those, 16 contain the word "Frame". Is that suggesting you a missing abstraction?

- Feature Envy: Scorer over Ball
Just like above, Scorer is 57 lines long excluding empty lines (35 excluding also braces). Of those, 15 contain the word Ball (and 6, the word Frame). C'mon. We're talking about bad smells here, you agile guys are supposed to fix this stuff.

(note: the two previous issues may come from hindsight, as in my design, I do have a Frame and a Ball class).

- "Scorer"?
Good ol' Peter Coad once talked about the "er-er Principle": Challenge any class name that ends in "-er" (e.g. Manager or Controller). If it has no parts, change the name of the class to what each object is managing. If it has parts, put as much work in the parts that the parts know enough to do themselves.
That's OOP 101, because if you need a manager, it's because you got passive objects, and objects are about behaviour, not just data. You need a scorer because you haven't got the right classes.

Overall, this is not code you may want to maintain. Wanna give it a try? Ok, try to change to rules to, e.g., 3-6-9 bowling or low-ball bowling. Or make your own random rule, like "the 7th frame is only allowed one ball". See how easy it is. Hey, don't give me that YAGNI look :-), you are supposed to embrace change :-)).
It's worth repeating that in a real case, you'll be much worse off: you won't have a lenghty transcript of the session, telling you about how they come to put together that code. You'll have just the code; except that, in real-world projects, code will be 3 or 4 orders or magnitude bigger. You'll have to figure out everything else just from the code. If that's the average quality, well, good luck.

Of course, having only code to maintain means cheaper maintenance. Well, more exactly, it means that the mechanical act of maintenance (modifying stuff that needs to be changed) is cheaper. Of course, maintenance is not just about the mechanics: it is (mainly) about understanding the existing, understanding the new, understanding how to get from here to there.
Some redundancy (between wordy requirements, high-level design, and code) can help. Indeed, when I decided to design & implement my version of what above, I found it more convenient to read the wikipedia page, not to reverse engineer requirements from code. Of course, I also used the test cases they wrote to corroborate my understanding of the rules. Some redundancy can be helpful. Just ask your favorite aerospace engineer :-)).
This is why I always teach people to look for the bright and the dark side of everything, redundancy included (which of course, has well known shortcomings as well). That's why I don't like "extreme" approaches that pretend there is no downside in making "extreme" choices, only benefits.

So, on the bright side :-)), the code above is short (excluding blank lines and excluding test code, it totals 102 lines). Indeed, in a usenet post RCM reports that people who have "generated code" from the devilish overdesigned :-) UML diagram and added behavior got a whooping 400 lines.
Big deal. That was (in the authors' intention) a sketchy domain model, not a sensible design model. And code generation is usually a scam. That has nothing to do with diagrammatic reasoning (quite the opposite, I would say: it's diagrammatic nonreasoning: more on this next time).
Back to the bright side of the above, that code uses only 2 classes, which could be good if you don't like diagrams (which would help you understand the collaboration between multiple classes). Appalling enough, considering that the two authors together have something like 70 years programming experience, it looks like beginners' code. Which could be good in some environments. I'll get back to this another time.

Ok, enough bad cop for today. I'll show you my stuff in a few days (meanwhile, you can grind your teeth :-). I'll also describe my "process" and then draw some conclusions.

Labels: , , ,