February 2008

Fun with C++: Macro Pitfalls, Batch 1!

(UPDATE: Added the 6th item.)

I’m a fan of C/C++ preprocessor macros. I would be very happy if C++ received macros on par in power with those of Common Lisp macros, but that’s only a dream! We have to work with what we have.
Anyways, with use of macros come a lot of potential pitfalls. I’m going to go over a couple of them. I’m sure any C++ programmer is aware of these, but the reiteration would hurt now, would it? I’m also hoping that with your feedback, we can complete these into something referable and usable.

  1. Operator Mishaps From Hell!
    This is a classic example.

    #define SQR(x)    x * x

    Imagine what happens when you use something like SQR(a + b). But as I said, this is a classic example, and any C programmer with half a brain should know enough to avoid this (doesn’t mean it always happens though. We all have our shameful secrets!)
    The moral is: always enclose every usage of macro parameters in parentheses as well as the whole thing. (#define SQR(x) ((x) * (x)))

  2. The Space From Hell!
    #define SQR (x)   ((x) * (x))

    Notice the space between SQR and the (x)? The programmer didn’t! It usually results in a cryptic compile error about something not being a function or about incorrect number of arguments. Easy for more experienced programmers to understand and rectify (usually with a bang to their heads.)
    Also, it’s not hard to imagine cases without any compile errors and severe oddities in runtime behavior. Remember: Compile-time errors and warnings are your friends as long as you pay attention to them! They might be a little hard to understand, but a project with 100 pain-in-the-neck compile errors is better than 100 hidden runtime bugs just waiting to cut your neck off.

  3. The Semicolon From Hell!
    Let me demonstrate the perils of the semicolon.

    // A macro to increment a counter, and wrap around to zero is a limit is reached.
    #define ADVANCE(x, wrap)    ((x) = ((x)+1)%(wrap));
     
    // Usage 1
    while (should_go_on)
        ADVANCE(i, 100);
     
    // Usage 2
    for ( ; should_go_on; ADVANCE(i, 100))
    {}
     
    // Usage 3
    while (should_go_on)
        p = ADVANCE(i, 100) + 100;

    As long as you stick to the first usage (i.e. as a statement,) you get away with your mistake (the spurious semicolon at the end of the macro definition.) However, usage 2 won’t compile. But this is a good thing, since you will know that you have an error in your macro (of course, if you’re a newbie in C++, it may take you quite a few minutes to figure this one out.)
    Usage 3 is where things get interesting. The code will compile (at most with a mysterious and seemingly unrelated warning,) but it won’t do what you think it does. That can be a bitch to track down.
    So, the moral of this story is: don’t put semicolons in macros!

  4. The Multiple Evaluations From Hell!
    #define SQR(x)    ((x) * (x))

    What happens when you evaluate the value of SQR(++a)? Does that seem alright to you? (By the way, any one who tells you a += b is equivalent with a = a + b is either a moron, or thinks you are a moron!)
    The lesson of this item is: don’t use macros as fast, generic functions! Use inline templates (which are even potentially faster (do you know when? :D )) Like this:

    template <typename T>
    inline const T & sqr (const T & x) {return x * x;}
  5. The Multiple Statements From Hell!
    The following macro deletes a pointer and sets it to 0, a very sensible thing to do nearly all the time.

    // This is not OK. Don't use this macro like this. Keep reading down this article!
    // Also note that I've not placed a semicolon at the end.
    #define SAFE_DELETE(ptr)  delete (x); (x) = 0
     
    // Usage 1
    char * s = new char [42 + 1];
    ...
    SAFE_DELETE(s);
     
    // Usage 2
    if (some_condition) SAFE_DELETE(s);

    But it doesn’t work, does it? I mean, usage 1 is OK, but you’ll wish you were never born when you have to stay up late into the night and debug the results of the second usage, won’t you? So, what do we do? I imagine everybody’s first response would be:

    // This is not OK, still.
    #define SAFE_DELETE(ptr)  {delete (x); (x) = 0;}
     
    // Usage 3
    if (some_condition)
        SAFE_DELETE(s);
    else
        doSomeWork (s);

    See the problem? No? The problem is in the semicolon before else. C++ doesn’t let you put a semicolon after an closing curly brace (’}') and before “else”. So what do we do? This is one of the solutions:

    // This is almost OK, and usable if you are not very picky.
    #define SAFE_DELETE(ptr)  do { delete (x); (x) = 0; } while (false)

    That’s right! We put the multiple statements inside a do/while block that is guaranteed to be executed only once! Brilliant, isn’t it? Well, I didn’t come up with it. I wish I could say that I though of it independently, but that’s not the case. I saw it in kernel code even before I had run into this problem! But the code didn’t say why it was used this way, and analyzing and discovering the purpose of this small piece of code was one of my biggest “Aha!” moments in C programming.
    So, we are good to go, right? Well, not quite. If you don’t care much about warnings the compiler gives off, you are good to go. But the above code results in a warning (only when you have turned the compiler warning level to the highest degree,) that the used condition is constant. Although the occasions for this warning are intentional most of the time (like here,) it may be the case that the compiler is warning us about a typo. Because of this, I don’t want to disable this warning (unlike those blasted 4996 warnings Visual C++ gives off like candy on Halloween, which I shut off with a wide and hysterical grin, while caressing my ax handle!)
    So what do we do? The best solution that occurs to me is like this:

    // We define a function that always returns false
    namespace __useless {
    inline bool i_am_false () {return false;}
    }
     
    // Now we use the above function instead of hard-coding the constant expression
    #define SAFE_DELETE(ptr)  do { delete (x); (x) = 0; } while (__useless::i_am_false())

    We trick the compiler here. The compiler is wise enough (or not smart enough) to give a warning no more, but any optimizer would optimize the overhead of the function call away completely.

  6. The “if” From Hell!
    Suppose we “optimize” the above SAFE_DELETE macro, to check whether the argument is NULL, and do nothing in that case.

    #define SAFE_DELETE(ptr)    if(ptr) delete (ptr), ((ptr) = 0)
    // Note that I've incorporated the comments from Hadi (comment 2) here, to make a point.
     
    // Usage 1
    if (some_condition)
        SAFE_DELETE (s);
    else
        return 0;

    Ouch! That hurts. I don’t think there’s anyone who doesn’t know this already, but in case there is (almost no shame in not knowing,) the “else” in the first usage will be the else clause for the “if” inside the macro! You are going to hate Dennis Ritchie after the first two hours of bewilderment over this (but of course, that would be out of frustration, and without logical cause!)
    You may be tempted to solve the problem like this:

    #define SAFE_DELETE(ptr)    if(ptr) {delete (ptr); (ptr) = 0;} else {}

    But don’t! Do this:

    #define SAFE_DELETE(ptr)    do { if(ptr) {delete (ptr); (ptr) = 0;} } while (__uselss::i_am_false())

    The moral? If you write “if” clauses in macros, be extra careful.

The above were only some of the pitfalls, but I can’t continue right now. Looking forward to your suggestions. Don’t be shy!

C++
code
updated

Comments (6)

Permalink

Empty and Free

Among all the great quatrains of Khayyam, I love this one the most. It may not be the deepest, or the most beautiful, but it’s the one for me:

  ماييم و مِي و مطرب و اين کنج خراب  
جان و دل و جام و جامه در رهن شراب
فارغ ز اميد رحمت و بيم عذاب
آزاد ز خاک و باد و از آتش و آب

Unfortunately, I’m too lazy to go and locate the Fitzgerald translation! I may do so one day. I guess I should thank a couple of my friends in junior high that sparked my interest in Khayyam poetry: Afshin Izanloo and Saeed Soheili. I still hear from them from time to time. Also, I have an interest in detective stories and crime fiction thanks to Afshin. Thank you guys!

Poetry

Comments (0)

Permalink

The Story of My Life

Zero. Pain, light, confused, ignorant, blissful, happy, deathly, confused, sad, happy, sad, happy, happy, lonely, ignorant, friend, happy, mediocre, lonely, arrogant, mediocre, lonely, friends, happy, lonely, dead, lonely, lonely, ignorant, lonely, mediocre, mediocre, aimless, stupid, confused, still dead, hopeless, wishful, hopeless, mediocre, stupid, pain. One.

The above was not the story of my life. That was one minute. This one is:
I make a terrible first impression. My appearance is often untidy and disheveled, I dress tastelessly, and I’m uncomfortable and out of my element in most social occasions. So, I have no hope of impressing anyone within the first two minutes.
For the next 118 minutes, if the “anyone” happens to be interested in a subject that I have read a Wikipedia article about at a time close to the encounter, I may be able to impress upon them my fluency in some geeky and usually absolutely mundane and useless niché of a topic. So, it sometimes works out in two hours.
On the course of the next (approximately) two weeks, and usually way before that, the person will notice that I’m not that deeply knowledgeable after all, and therefore, the minuscule interestingness of my character will disappear and so will the relationship. If that doesn’t do the trick, my severe negligence of personal hygiene will. So that’s your two-week milestone.
If they have a heart of gold (the personality trait, not the spaceship - although it would be cool to know someone with an actual Heart of Gold!) they might stick around, for 6 more weeks of torture by bad breath and uncomfortable social occasions, and they may see a kind and sad slob, trying way too hard. Then we might become friends, after two months.
For close to two years, my friend will see so much dishonesty, abuse, cheating, laziness, weirdness and unpunctuality that he/she would start cutting off the unstable bond. Two years. *gong* (or *whistle*!)
So, think about were you are! Think before thinking otherwise.

rants

Comments (2)

Permalink

Silent Resurrection

If you know what it means for me, don’t tell anybody that doesn’t know!

life

Comments (0)

Permalink

On the Brink

Mike is dying…

One of his hard drives is unusable already. The oldest harddisk on Mike was acting erratically already, and the SMART reports were not hopeful at all. The 120GB disk’s sector 0 is unreadable now, and along with is goes almost 3 years of customization on the Windows XP, a lot of stored information in the form of browser history, bookmarks and download lists. My downloaded email is now out of reach in the Thunderbird profile directory as well (although I’m not entirely convinced that it’s lost.) Around 20GiB of unclassified music is also trapped on that drive.
Another of the hard disks, a 300GB hosting some of my oldest and most favorite movies, is also making strange noises.
I tried to install Windows XP on a new disk, a 500GB WD, but I found out with heartache that Mike’s mainboard (a Gigabyte K8NS Pro) can’t (or won’t) boot from any of the 320 or 500 GB hard drives. It boots off a USB disk, but not from a standard SATA hard. Windows XP doesn’t even list the larger drives in it’s installation partition selection list. Fedora Core 8 is better, and it installs, but it won’t boot. I have three different SATA controllers (nForce and SiL are integrated in the chipset, and I’ve added an unnamed SATA card) but the larger drives remain unbootable.
Mike also doesn’t allow any more RAM than the 1GiB already installed. This happened a few months back. I’m guessing the empty RAM slots are damaged or something (maybe by dust.)
All in all, Mike is on life support. I’ve learned so much and done so much with him, that now that I’ve turned him off, after close to 3 years of almost continuous upness, the absence of the whirring sound of the 8 fans in its case is driving me crazy. It’s like I’m told that my leg needs to be amputated, and I’m hoping against hope that I can keep it.

life

Comments (0)

Permalink

The Easy Way

Life becomes so full of misery sometimes. There are always more miserable people than I, and the are always bigger problems than mine, but maybe those people have more tolerance, or those problems are faced by people far braver and more able that I am. And of course, misery is such a local feeling in time, that my misery of today will look like a child scraping his knee tomorrow.
I feel like the whole content of the world have turned into crap and it’s raining down on me. It’s all my own doing. There is no one else to blame. I wish there was. I wish there was a God, that I could point to and accusingly ask back my destiny. A God that I could shout and scream at like madmen, and break my teeth off and throw at Him to get his attention and maybe get back my life; or get back all our lives. Maybe I could tear off my rib cage with my hands and a short screw-driver with green handle and use the rib bones as knives and shanks to intimidate Him.
I was sitting in a plane today, and I was hating all those around me, because statistically, all of them were stronger than me and more able to deal with their problems (last time I checked, any absolute value was statistically greater than 0.)
While Pink Floyd were performing “Sorrow”, I was hoping that the airplane would crash, and only one person would die in the crash and the ensuing explosion and fire, and that one person would be me, because I was wishing that the airplane would crash, and everyone in it would die, and the charred bodies would be so damaged and mangled that no one would be able to see that the passenger in the seat 27D was having a big smile on his face, in spite of his head being almost completely severed from his body and only hanging from a thread of sinew, and his whole body fat and skin being melted into the seat in the fires and explosions.

rants

Comments (3)

Permalink

Java Ruining the Programmers of Tomorrow?

Read this post on slashdot, and this article that the post references. The comments on the /. page is interesting as well.
In short, we all know that today’s university graduates learn very little about programming and software architecture that has real value from their curriculum. This article focuses on the problems of high-level programming languages, but the reverse is equally true. Again we have all seen old-timers who can code in assembly and C, but are still struggling to grasp this “new” concept called Object-Oriented Programming!
I’m sure that for great programmers, a language is just a window, and not their eyes. They can use a different window in the other wall if the current one does not offer the view they desire or has the wrong-colored glass panes, but for the rest of us, our programming languages pretty much define what we consider easy/hard/fun/boring (if not possible/impossible.)
That’s one of the reasons I like and prefer C++. It hides almost nothing of the complexities and idiosyncrasies of the underlying machine (which can be a pain, or an opportunity) while still letting you solve, or at least manage, the pains and take advantage of the opportunities. It’s mighty complex and full of dark corners and dirty tricks, but you can get the job done, on any level you want, as close or as far as the bare metal as you want.
In short, C++ rocks!

programming

Comments (12)

Permalink

His Dark Materials

Phantasmagoria, Mortal Combat (forgive me for the ‘C’!) and Twisted Metal!

These have two things in common. First, they are all old games that I found exceptional in some way and enjoyed very much. It was at least 10 years ago since I played any of them for the first time (and many years since I played them last) but their effect is still etched upon my memory. Mortal Kombat, the first two installments on SEGA Genesis, is the easiest of them all to recognize for even non-gamers (who could forget “Sub-Zero Wins” or “Fatality” in that deep voice?)
“Twisted Metal II” on the PlayStation (or PSOne if you prefer) was an immensely fun game (search Wikipedia yourselves; I’m not providing the link!) and the memory of the school hours I ditched (or the after-school hours I didn’t) to play at the arcade nearby with a couple of friends gives me a welcome nostalgic pang.
Phantasmagoria is the far lesser known of the three. It was a multi-CD (Wikipedia says 7(!) but I can’t say I remember exactly, since I was never able to finish it) interactive-movie (like Hard Line, or the Mad Dog series) adventure game. Very dark and scary and disturbing.

The other thing these three phrases have in common is that they are all used in the book “Northern Lights“, the first book of the fantasy trilogy “His Dark Materials” by Phillip Pullman. It’s not every day that one encounters an (allegedly) children’s book with genuine cruelty, malice and violence (another notable exception is Lemony Snicket’s “A Series of Unfortunate Events”.)
I’ve only finished the first book, and just begun the second, but it seems hard to me that the series can arrive to a conclusion in merely three short books! In any case, His Dark Materials is an entertaining series with some unique features (e.g. the introductions of “daemons”.) Anyone interested in short fantasy series should give HDM a try.
Another interesting feature of the series for me is Pullman’s opposition to organized religion, in this case Christianity. The “Church” in his books is somewhat reminiscent of what the church would have been like at the beginning of 20th century if it had survived the Renaissance.

books

Comments (0)

Permalink