What was that macro?
I find myself asking this question a lot: "Now what was that define that detects annoying platform that makes porting otherwise perfectly-portable code difficult?"
This will narrow your search down to something useful:
echo | cpp -dM
Go ahead, try it. I wish I'd known this one earlier.
Network Programming
If ever you find yourself wanting to do some network programming in C, C++, or any other language that uses the socket paradigm, you must be careful. You are only inches from the edge of the precipice of insanity.
Luckily, some guy who calls himself Beej wrote a fantastic guide to network programming. He takes you by the hand and leads you gently away from the precipice. His guide is fun and easy to grok, but doesn't water the topic down. It's a fine piece of writing.
Even if you're writing Ruby network code, for example, which streamlines much of the insanity (thanks to exceptions and some nice OO abstractions like TCPServer), the socket concepts you will gain from at least skimming this guide will be a huge help.
Duck Typing Defended
Ever since I discovered ruby I've loved dynamic typing. I never paid much heed to people who got their knickers in a knot over how dynamic typing is "unsafe".
While working on Ardour this summer, I've literally been wrestling with C++, all because it is statically typed. The code I've been writing over the past month could have been done in one week with half my brain tied behind my back (which is the usual situation, by the way), if only C++ were dynamically typed.
In other ways static typing hasn't exactly got in my way, it just causes me to jump through hoops that seem silly, unnecessary, and inelegant. A simple concrete example will help. I'm doing undo serialization, and of course Ardour already serializes other things in order to save sessions. In C++ this means lots and lots of otherwise unrelated things all inherit from a common serializable base class. This is why multiple inheritance is necessary in C++. In Java you can use interfaces, which is better than multiple inheritance but not by much. In dynamic languages, you don't even have to give it a second thought. You just make sure everything that needs to be serialized responds to a serialize method, et voilá!
So as I've been wrestling I've had this nagging feeling that it's *me* that's broken, not C++. Have I become so spoiled by dynamic languages that I can't program efficiently in a static language anymore? Are the static people right and one day my whole world will come crashing in and all my ducks will keel over? Feelings of self doubt are part of being human, and these were some of mine this summer. Then I read a blog post by Bruce Eckel on Strong Typing vs. Strong Testing and my mind was put at ease. The thrust is, dynamic typing is good, and testing is necessary. I think this quote about Robert Martin (a C++ guru) sums things up nicely:
Robert came to more or less the same conclusion I have, but he did so by becoming "test infected" first, then realizing that the compiler was just one (incomplete) form of testing, then understanding that a weakly-typed language could be much more productive but create programs that are just as robust as those written in strongly-typed languages, by providing adequate testing.
I have a thought along these lines. Let's say you've got duck typing in full swing as in his examples. Now in the real world it sometimes happens that you choose a common ambiguous word for the duck method, like "run" perhaps, but more often than not you have methods that are named fairly specifically, like "speak" or "jump". The static argument is that you might accidentally call the method on something that won't do what you anticipate because it's not of the right type. But really now, how many of your objects that implement "jump" are going to be the wrong kind of objects in your application? Often the answer is none. So you jump through hoops for no reason.
Do read the article, it's fun and well-written. Even if you come out still clinging to your beliefs that static typing is Good and Wholesome, at least you'll see that us dynamic people aren't necessarily lazy, stupid heathens.
Posted in cs | 2 comments |
Constructors, coercion and casting, oh my!
This little bit of C++ trivia I may have known once but had forgotten. If you have the appropriate constructor, the compiler will happily coerce for you:
class Foo
{
public:
Foo(int);
int i;
};
int bar(Foo f)
{
return f.i;
}
int main(void)
{
return bar(42);
}
You can also explicitly cast, which is useful when you are feeling explicit, or want to cast to a subclass for polymorphic reasons.
Zero Link
XCode has a feature called Zero Link, which apparently more or less skips the linking step when you build your project. I don't know the details but it's probably fair to say that it's like just-in-time compiling, but for linking. The raison d'être of this feature is to reduce the time spent waiting for things to compile during development. It's not to be used for released code, so it is on by default in the development building style and off by default for the release style.
That sounds really neat, but we just wasted a bunch more time than we could possibly have saved with Zero Link, because there was no linker to complain about this:
// foo.h
#ifndef FOO_H
#define FOO_H
short foo[2];
short *p;
#endif
The problem, if you don't already see it, is that those variables should be
declared extern and instantiated in one and only one .c file. ld would
complain about duplicate symbols when trying to link two .o files that had both
included this header file. But with Zero Link, there's no error. Instead, you
get crazy behavior from the debugger: assignments that seem to have no effect
and other odd things that make you think you've fallen into an alternate
universe.
So be careful.
Posted in mac | no comments |
% for Remainder
I had my lunch handed to me today because some C DSP code I had written was wrong:
/* M is the size of the buffer,
* w is the base pointer,
* p is the pointer into the buffer */
void wrap(short M, short *w, short **p)
{
if (*p < w || *p >= (w + M))
*p = w + (*p - w) % M;
}
What I did not realize is that the % operator in C does not wrap negative
numbers into the positive range like you would expect if you were a
mathemtician. i.e. -7 % 8 == -7 in C, where in mathematics -7 = 1 mod 8.
I can hear you now: "Didn't you test it, you fool?" Well, yes, I tested the algorithm in Ruby, where mathematics hold true:
rb(main):001:0> -7 % 8
=> 1
How was I to know the C version was braindead?
So which is right? Well you can probably guess my bias by now. But inquiring minds want to know, and no other type reads this blog so I did some research.
First, the mathematics. Wolfram has a dizzying explanation. Search for modular arithmetic for any number of treatments of the subject. Too lazy for that? Fine, look at your watch. If it's 10:05 and you go back in time 10 minutes, is it -5 past 10? Arguably so ("5 to"), but most people would agree that it's actually 9:55 by canon.
Now for the C argument. The 1999 ISO C Standard says:
If the quotient
a/bis representable, the expression(a/b)*b + a%bshall equala.
So my compiler is fine. It's C that's broken. Before C99 the use of % on
negative numbers was IMPLEMENTATION DEPENDENT, which if you know anything about
C history means they didn't think it through well enough, or they made a
decision based on speed or ease of implementation. The C99 definition was
probably chosen either for ease of implementation or for the most common case.
Not exactly good enough to convince me.
Naturally, there's no going back now, so if you find yourself possibly needing to do modular arithmetic on negative numbers in C, be sure to add again if negative:
/* M is the size of the buffer,
* w is the base pointer,
* p is the pointer into the buffer */
void wrap(short M, short *w, short **p)
{
if (*p < w || *p >= (w + M))
*p = w + (*p - w) % M;
if (*p - w < 0)
*p += M;
}
Here's a coherent post
to the gcc-help list about the subject. Now, I don't want to hear anyone saying
that % is the modulus operator from now on. It's the remainder operator.
Posted in cs | 3 comments |