View Single Post
Old 03-13-2003, 12:43 PM   #13
angelbob
Member
 
Join Date: Feb 2003
Location: Bay Area, CA, USA
Posts: 39
angelbob is on a distinguished road
(Quotes here are from Yui Unifex)

This is true, and I've seen some impressive similar stuff done with using pointers for transparent reference counting. It's pretty nifty as long as no bug ever exists in it (hint -- check every implementation of pointers for refcounting that you use... When reassigning, do they increment the new reference count BEFORE decrementing the old one?).
Of course, if a bug ever happens that doesn't crash your code, you have to debug it. I'm a big fan of knowing when functions are called. C++ experts can generally tell you the order of function calls in, say, this:

C *= myType(9,4,"bob")

It definitely gets hard to follow, though, especially if (in the example above) you haven't defined your own *= operator and C++ has to wing it. It will, in fact, do so, but the choices it makes for this and for typecasting aren't always intuitive, and a lot of stuff (like typecasting) happens silently in conditions that are just weird.

That doesn't address your point, though. Your point is that C++ will fake a builtin data type and make it call arbitrary other functions silently to an extent that C won't.
Yep. You're right. And it's a massive debugging headache that it'll do so.
Perl does this "right", too. Perl is the only language I know with a worse reputation for syntactic and semantic cleanliness than C++. Coincidence?

Fair enough. I'd argue that not much inheritance is appropriate for Server, Connection and Player objects -- there usually aren't very many kinds of them. But you're right, they're not physical.

Well, sort of. That doesn't change them if the parent changes, which is my favorite use for data inheritance -- a tree of archetype objects. Skotos' ur-objects are an example of this. Anyway, back to C++.

Yes. Either one will have to build its own inheritance, effectively. It's just that the C++ people will yell at you for not using the built-in language inheritance and then stonewall when you ask "so what do I do about inheriting at runtime?" :-)

But seriously -- true. I still argue that C++'s extra complexity isn't generally worth the benefit in any environment with more than one coder. When you say that C and C++ break down to the same thing for a given problem, I argue that that supports my argument. They wind up using essentially the same methods to deal with the problem, but C++ has all the extra nastiness you're not using at the moment.

But somebody else on the project may be, and it's allowed to mess you up.

Actually you'd just need an explicit function call rather than implicit one, or a function that copies and passes it back (which is the same thing, basically). But you're right, it wouldn't happen automatically, which can definitely be irritating at times.

I won't argue with you over this one. String should probably have been built into C long ago. Of course, it should also have been built into C++ (or at least the standard library) long ago, and that hasn't happened either.

Clever. By calling this "lose functionality" you skip over the fact that it's just syntactic sugar. Nonetheless: it's just syntactic sugar. C++ used to be implemented with a preprocessor over C, and you know, it wasn't even a very complicated preprocessor. Big, but not complicated.

And to get the speed of an unchecked int you'd have to add an extra function call for unchecked assignments and go through the headache of remembering which you're using. Plus, you can detect errors if you get a problem in a function call. How do you detect an error in the middle of "equals"?

That's true. That's another thing C can't achieve. Looking at the implementation of templates, I frequently sigh in relief that C can't "achieve" that. While this is probably relatively clean with your "checked int" example (when's the last time you genuinely used one of those, BTW?), I'd argue that it's fundamentally backwards to do the same thing with vectors, matrices, or anything where using "+" or "*" is a stretch for the operator involved. It makes for clever, small code (and massive binary size since you're using templates) but you're fundamentally applying very different concepts to a different set of objects using the same syntax. Urgh.

That's only true in cases where the problem domain is well-defined and well-documented, and already defines + and * unambiguously. There are two ways to multiply a pair of 4x4 matrices -- only one is written as multiplication by actual mathematicians, though they don't actually bother to put a star. In fact, if they *do* put a star it frequently means they're talking about inner product, which is a very different operation.

But for most things, it's a lot less obvious what + or * represent. Using operator overloading for math is reasonably clean. The vast majority of the time it's not used for math.

For instance, have you noticed that cin and cout use the bit-shift operators? Eeeeeeewwwww! And that's just in the standard library! You could argue that it sorta makes sense ("look, they're little arrows. Kinda. And arrows mean input and output. Kinda. And we didn't allow overriding the actual arrow, plus it doesn't come in reverse flavor."). But if you did, I'd laugh at you.

I've found that's a better example of the way people choose and overload operators in the real world, which is why I don't touch other people's C++ code any more. There's one exception, but Tom's even pickier than I am about this stuff and he rewrites everything three times to get a clean design.
angelbob is offline   Reply With Quote