6/10/2010

06-10-10 - Ranting

I really hate programmers who whine about other people's code or the compiler or the OS or whatever, always blaming other people for their woes. 99% of the time their own code would not stand up to the standards that they are holding others to, and lots of the time the thing they are whining about is actually because of their own bad usage of a perfectly good system.

That said, I will now do some ranting.

Fucking external code and your use of unsigned ints! I've tracked down the last two (*) of my nasty bugs in porting to 64 bit and both were because of people using unsigned ints for no good reason. (* = I think; you can never know that you found your last bug, much like you can never prove a physical theory right).

One was in the BMP reader code that I stole from MSDN years ago after getting fed up with not being able to load some weird formats (RLE4 or whatever). (BTW this is another rant - every god damn video game codebase I've ever used has some broken ass home-rolled BMP reader in it that only handles the most friendly and vanilla BMP formats; fucking don't roll your own broken ass shit when there are perfectly good fully functional ones you can take). Anyway, because it's MS code it uses the awful DWORD and UINT and all that shit. It actually had a number of little bugs that I fixed over the years, such as this one :


<              dwSrcInc = ((dwWidth >> 3) + 3) & ~3;
->             dwSrcInc = (((dwWidth+7) >> 3) + 3) & ~3;

but the nasty one was the inherent type of dwSrcInc. BMP's of course can scan up or down so there's some code that does :

if ( normal ) dwSrcInc = pitch;
else dwSrcInc = - pitch;

Looks good, right? Well, sort of. Of course dwSrcInc is a DWORD. What happens when you put a negative in there ? Yeah, you're getting it. We then update the pointer like :

ptr += dwSrcInc;

Well, this works out the way you want if the pointer is 32 bits, because the pointer math is done like an unsigned int add and it wraps. But when the pointer is 64 bits, you're adding not quite 4 billion to your pointer. No good.

While I'm at it, this is the list of nasty not-obvious stuff to watch out for that I made :


64-bit dangers :

use of C-style casts, like :

    int x = (int) pointer;

use of hard-coded sizes in mallocs :

    void ** ptrArray = malloc( 4 * count );

structs with pointers that are IO'd as bytes
    or otherwise converted to bytes as in unions etc

use of 0xFFFFFFFF and similar

use of (1<<31) as a flag bit in pointers

struct packing changes

ptrdiff_t is signed
size_t is unsigned !!

unions of mismatched sizes do not warn !!

8 comments:

Autodidactic Asphyxiation said...

I don't understand why people don't wrap up their bit-bashing in some way. The fun part might be thinking of the clever way to do something, but the responsible thing is to engineer the code so that no one needs to really understand it. For example, it took me a less than a day to write my tagged-pointer/string class, but I spent a whole bunch of time testing/documenting it so that people could understand how to use/extend it, and making it work with different endianess/pointer-sizes without resorting to giant #ifdefs. Oh, and my "size" field is just an "int."

If you're not doing all that, then the value of the code you're adding (which might already be 0 if you're rewriting something that works) is mitigated by the maintenance burden/deferred defects you've added.

This is yet another reason why code review is useful. The problem is that the benefit of such practices is not necessarily within the time horizon of the actors. It is kind of nice working at a company that takes the long view on such matters.

nothings said...

The problem is that the benefit of such practices is not necessarily within the time horizon of the actors.

That cuts both ways, though. Which matters more, the property of some company that you once worked for, or (insert whatever the appropriate contrast is here, e.g. "or your personal creative satisfaction")?

That is to say, if you are doing work for such a compony, then yes, maybe that is the best thing to be doing (since the company is paying you for it). Rather I am questioning "it is kind of nice working at a company that takes the long view on such matters", since that means you're working for a company that makes you make that trade-off.

cbloom said...

" The problem is that the benefit of such practices is not necessarily within the time horizon of the actors."

cbloom said...

.. I don't believe that's actually the problem. The problem is that the actors *think* the benefit is not in their time horizon, but they are incorrect, eg. they are thinking too short-sightedly.

Autodidactic Asphyxiation said...

Yeah, I think people underestimate how long code needs to be maintained. There's this hacker mentality some people have to Get Shit Done and move on. There is some merit to that, but it is also a good way to create headaches down the road. The other thing is that many of these headaches are totally avoidable. Personally, I tend to err to far on the "quality" rather than "quantity" side, so for me it doesn't feel like much of a tradeoff, but I'm probably in the minority there. It's also been a long time since I've had a hard deadline to meet, so I don't think I've been as tempted to take a self-defeating shortcut.

Anyway, maybe it isn't the right thing all the time, but it is certainly the right default. It seems like the only sane choice if you are working on code that is intended to be reused and/or has non-trivial interface choices.

cbloom said...

Okay this getting into a tangent, but I've been realizing recently that there is basically NOBODY in the world who actually codes right.

It's one of those issues that psychologically, the middle ground of perfect coding is just too hard for humans to maintain, it's much easier for our brains to be too extreme one way or the other. (the same is true of politics, religion, courtesy, etc. etc. it's too hard to be reasonable and balanced and rational about every issue, instead we just tend to turn ourselves into stereotypes and extremes which are easier).

In particular :

I believe the right way to code is to initially do a very quick hack to get things working, do some prototypes, make sure your idea works, refine it a bit, then immediately go back and clean it up nicely and make it robust and comment it and all that.

It should be a constant process, think of code as like quick dry cement, and as each portion of the code is starting to "dry" (that is, become part of your longer-term structure), you clean it up and make it robust, but then you slap down new bits of the code in wet cement and don't bother making those nice until you're sure they're right. So every day you might do a bit of hacking and then go back and clean up a messy bit that is becoming permanent.

Nobody does this. Not one. Not one single coder I've ever met or seen or heard of actually does this.

People are either too clean right from the beginning (eg. me and Won) which causes a little bit of wasted time making things clean that don't need to be or that get tossed out when something is rewritten. Or people are good quick hackers, but they don't actually go back and clean things up - not quickly enough.

Furthermore, the optimal coder should tune their degree of cleanliness to the case. That is, code which will last longer should be cleaner, code shared with more people should be cleaner, etc.

Again people generally don't do this. Most people wind up adopting a "style" which is just the way they write code, and it doesn't vary much.

cbloom said...

I think another interesting variation in people is the amount of personal responsibility they take for this (the naysayers would call this masochistic self-blaming).

If I have a fight with my girlfriend, I don't think "yo that girl is whack", I think "what could I do better next time".

If I write some shared code and it causes other people problems, I don't think "pff they are dumb and using my code wrong" I think "could I write this code better so it was easier to use". Especially if somebody has a crash or weird bug or something because I didn't protect my code well enough from misuse, I see that as my fault that I caused them headaches, and I consider giving other people headaches one of the worst sins you can commit in life.

Every time there's a problem and I'm even partially involved in it, I think "what about my actions contributed to this and how can I do better in the future".

Now I'm not saying that this is a better way to be. Often it causes needless anguish and contemplation when in fact quite often other people are just being dumb or whack or whatever. I see other people who have more of a life attitude of "whatever, they have problems, I'm fine, it's not my fault" and they seem to have less stress and less self-doubt.

Autodidactic Asphyxiation said...

"I believe the right way to code is to initially do a very quick hack to get things working, do some prototypes, make sure your idea works, refine it a bit, then immediately go back and clean it up nicely and make it robust and comment it and all that."

Yeah, I did this for the first pass, which is probably the most crucial. But as time goes on, interfaces will accrete crap, invariants might get compromised, and it isn't clear who will be the next curator. You kind of have to trust the imperfect system to make sure it doesn't get too bad.

old rants