RSS LJ

September 23, 2004

Cruft ()

by fluffy at 9:21 PM
Argh. Crunch time means just adding on more and more layers of how to do complicated things in a slightly less complicated way, in a way which makes simple things really fucking complicated.

On a more annoying note, earlier I was asking James why he was doing a certain stylistic thing, and he said "Well that's what Stroudstrup says to do." Rather than debating it on its merits. Which is fine, but then we got into a little chat about stylistic stuff, and then he started to bitch about how I need to comment my code more. Never mind that he doesn't comment his code at all, and he likes to make lots of indirection and just these hugely retarded call graphs which are impossible to trace, and moreso need to be traced backwards quite a bit to see what's supposed to happen where (to try to figure out why something isn't happening).

He clearly doesn't understand state machines, for example, but he gets offended when I try to do something in a clean way (he claims that I'm just upset because he was "touching my stuff" or that his way is just as good as mine). Ugh.

At this point I'm resigned to just let him do whatever crap he wants and just save the refactoring for the next game, when we actually have time to do it right (and when a lot of this will be moot since the next one will be in 3D anyway, which is a totally different beast on the DS).

Most of the duct tape he's added to my stuff has been because he doesn't understand the system or what I was getting at, not because my system was incomplete. One of the major changes he made today basically took a huge shit on the chest of a simple event-based API and turned it into a huge fucking tangled morass which makes it really fucking hard to just change the palette. Unless you happen to be doing stuff like changing the palettes and background images on both screens in the exact way that James happens to do everything.

I don't suppose he's ever understood the concept of "orthogonality." It's clear from his code that he doesn't.

(Also, the thing earlier that the long debate was about was the merits of using 0 vs. NULL for "a pointer which points to nothing." It's a semantic difference, but he insists that we should use 0 "because that's what Stroudstrup says," never mind that's not what ANSI says, and there's a huge semantic difference between 0 and NULL. (My argument: 0 is a number. Numbers are still things. NULL, although implemented as 0 on most systems, means "nothing." 0 is still something. His argument: "Stroudstrup says that 0 is more portable." QED, I guess.)

He also loves to harp on me for using post-increment instead of pre-increment on loops. Yes, I know that pre-increment is faster for overloaded operators, but there's no difference for fundamentals (I always use pre-increment for overloaded operators) and on C compilers set to strict ANSI mode, pre-increment has a different semantic meaning (it does the increment before the loop body). Never mind that the only real reason for avoiding pre-increment is a (very very slight) performance hit, while he sure loves himself a whole bunch of nasty shit which definitely hurts performance way more while also making the code less maintainable. HOORAY.

Ugh.

Honestly, I hope our little company grows so that we have separate dev teams in the future. I want to take on a more leadership/architectural/platform-specialist role and don't want to actually, you know, interact with James anymore.

Maybe next time around it'll be more like I'm in charge of the lower-level stuff and they get to build whatever morass of crap on top of it. Le sigh.

Comments

#3578 09/24/2004 12:12 am
Isn't NULL usually just defined as ((void *) 0) anyway?

(or is the situation more complicated than that?)
#3579 09/24/2004 04:31 am
It is, but it's a matter of code readability.

Like, sure, you could use 1 and 0 instead of true or false on a bool, and it would be "more portable" to do so, but that doesn't mean it's a good idea.

The thing which irritates me most about his justifications for doing things is that it's always an appeal to authority, and not based on any real experience on his part. "Stroudstrup says to do it this way because it's more portable." That's it, and no explanation on how it's more portable. When I challenge him on that, and point out that it's just Stroudstrup's opinion and it's actually not more portable (since every system has NULL defined, but not every system has NULL == 0 — the Motorola 6811, for example) he just says "But Stroudstrup invented the language!" which is only partially true. I mean, something that K&R said about the C language back in the 70s doesn't necessarily apply today, either. [edit: by this I didn't mean that NULL == 0 returns false on these systems, but that NULL is defined as something other than 0.]

Also, 0 is definitely not more portable; sure, it'll work on any C++ compiler on any platform where NULL == 0, but what about portability between languages? When converting code from C++ to, say, C#, Java, or even Managed C++, NULL translates to NULL, but 0 doesn't necessarily translate to 0!

Oh well. At this point I've decided that James needs to learn real-world software engineering practices on his own, since obviously he doesn't think my experience is valuable. I just wish he'd chosen a better time to assert his independence than two weeks before our game ships.
#3580 Ali (unregistered) 09/24/2004 06:18 am Much Ado About 'Nothing'
I always use 0 myself... of course, I also virtually always use GNU C as well.

But it's low priority. If I was working with someone who wanted to insist on NULL, I'd just shrugs and use it.
#3581 09/24/2004 06:30 am
Neither James nor I were trying to convert either to each other (though he was probably assuming that I was trying to convert him). I was just asking him why he chose to do it that way, and I found his answer interesting.

The compiler has little to do with it. The platform is more important. GNU C is available for the 6811, but the RAM address space on the 6811 begins at 0x0, and it's perfectly valid to have a variable there. It's an extreme example and a pretty major stretch as an edge case goes, but it's still a valid example nontheless.
#3583 celeriac (unregistered) 09/24/2004 08:55 am NULL == 0
The standard requires that null pointers compare equal to zero even if the architecture's null pointer is not bitwise zero. What is the 6811 compiler where NULL != 0?

Portability-- some systems #define NULL 0, other systems #define NULL ((void *)0). (and these are the only two definitions allowed in ANSI C.) Switching betweeen these definitions can cause problems.
#3585 09/24/2004 09:12 am
Comparing equal to 0 isn't the same as being numerically 0. (Yes, I know in ANSI C it is, but we're talking C++ here.)

I don't know of any specific 6811 compiler which makes NULL != 0, but I was just saying that 0 is a valid address for a piece of data on the 6811. Actually, isn't it also valid user address space on the 6502? (The 6811 is basically an extended 6502 intended for low-power small-footprint embedded systems, FWIW.)

A compiler could conceivably define NULL as, say, some variable outside of the address space, and then have a special-case numerical compare between that special pointer and regular pointers. Or it could just require that no pointer ever points to 0. Or whatever.

But this is still largely tangential to my main point, in that 0 is a number, while NULL is a meaningful symbol. When you see code like

m_mainBGFile = 0;


what does that tell you right away? You'd have to look at its type and see that it's of type FileHandle, but then what does that imply? That maybe FileHandle is an int typecast and this is the first handle in a series of files? But then if you look up the definition of FileHandle and see that it's a typecast of const Filesystem::Handle* then you realize that oh, this is actually saying "this is a null file."

If it were simply

m_mainBGFile = NULL;


then the first-glance response would be, "Hey, this file is being set to nothing," without needing to know anything extra about m_mainBGFile's type.

Looking at 0 vs. NULL in terms of the low-level implementation details is losing sight of the forest for the trees. Which, I think, sums up many programmers' problems nicely.

Oh, and the typeless vs. (void *) portability issue is largely a red herring, because you're only supposed to use NULL for pointer types anyway - if you're setting an int/float/whatever to NULL then you're falling into the exact opposite problem of using a symbol for something which it doesn't mean. The (void *) version of NULL was added later specifically to make NULL typesafe with pointer types and type-incompatible with fundamentals.
#3587 Pb (unregistered) 09/24/2004 03:19 pm I think the problem here
is that either of you actually care.

*waves*
#3589 celeriac (unregistered) 09/24/2004 03:32 pm
I agree about the meaningfulness of NULL vs. 0 in reading code. Just saying "not every system has NULL==0" is also a red herring.

m_mainBGFile = 0;


Yeah, but this is incomplete use of Hungarian. Call it m_pfhMainBGFile and that problem goes away (at least for some sadistic definition of "problem goes away" Very Happy)

Interestingly enough, what Stroustrup actually says is that if you use NULL you should define it with

const int NULL = 0;
#3590 Skywise (unregistered) 09/24/2004 06:24 pm Stroustrup say:
"In C++, the definition of NULL is 0, so there is only an aesthetic difference. I prefer to avoid macros, so I use 0. Another problem with NULL is that people sometimes mistakenly believe that it is different from 0 and/or not an integer. In pre-standard code, NULL was/is sometimes defined to something unsuitable and therefore had/has to be avoided. That's less common these days."

>amen<

Actually I switched from using NULL to 0 in my C++ code for two reasons: First because I agreed with Stroustrup (I don't like excessive MACRO use for replacement of immediate values like that... ) and figuring that NULL= 0 was part of "the standard" now, I could safely drop it. (also, rightly or wrongly, my style is to do if(!x) over if(x==0) which is more readable to me so I'm depending on NULL pointers being 0) Secondly I had been in a situation, long ago, where I was using two different C libraries, one that defined NULL to 0 and one that defined it to -1 (the latter probably being coded for another platform as Fluffy points out). Not a real problem, but there was a situation where both include headers for the libraries got into the same file and much hilarity ensued..

I think they both wash in pros/cons in normal use. I *like* my style and its legal but if I was coding for maximum platform compatibility I'd probably go back to using the NULL (and severely curtailing my C++ feature use...)
#3599 Ranieri (unregistered) 09/26/2004 05:03 am
I agree with fluffy all the way, but since the "0" camp seems to be more prevalent these days, I usually end up bowing to their integer overlords just to keep consistency intact.

But they do not mean the same thing, even if the compiler thinks they do!
#3600 09/26/2004 04:33 pm
celeriac
Interestingly enough, what Stroustrup actually says is that if you use NULL you should define it with

const int NULL = 0;


That's kinda dumb, IMO. It should be done as

const void *NULL = (void *)0x0;


because, again, the whole point to having a pointer called NULL is that it's a pointer. Treating ints as interchangeable with void* kills type safety in a big way.
#3602 09/26/2004 06:06 pm
fluffy
That's kinda dumb, IMO. It should be done as
const void *NULL = (void *)0x0;

because, again, the whole point to having a pointer called NULL is that it's a pointer. Treating ints as interchangeable with void* kills type safety in a big way.


Right - if you've turned off pointer vs. integer typechecking, then you may get bitten by something stupid like the difference between
int **x = (...);
*(x[72]) = 123;

and
int *x = (...);
x[72] = 123;

Don't ask me why you'd ever have an int ** that isn't an array at both levels, though.
#3603 09/26/2004 06:11 pm
int ** could be a C-style reference to an int * which the function needs to be able to reallocate or similar. James always uses C-style references in that case because he doesn't seem to be able to wrap his head around typing something as a foo *&.
#3615 celeriac (unregistered) 09/27/2004 11:07 am
fluffy
That's kinda dumb, IMO. It should be done as

const void *NULL = (void *)0x0;


because, again, the whole point to having a pointer called NULL is that it's a pointer. Treating ints as interchangeable with void* kills type safety in a big way.


Yeah, I was kind of scratching my head over that as well. Turns out ANSI C++ actually prohibits using a void * for NULL. I think the reason is the difference in typing-- a void* will not automatically convert itself into the needed pointer type like it does in C. So if you define NULL as a void * you have to cast it every time you use it.
#3616 celeriac (unregistered) 09/27/2004 11:15 am
Additionally I don't think it breaks pointer vs. integer type safety generally. They make a small exception to handle it:

An integral constant expression rvalue of integer type
that evaluates to zero (called a null pointer constant) can be con-
verted to a pointer type. The result is a value (called the null
pointer value of that type) distinguishable from every pointer to an
object or function.
#3619 09/27/2004 12:53 pm
Cruft on cruft on cruft. Ugh.