&. |

A software developer’s musings on software development

Macrolicious

Warning: I wrote this blog in 2008. That is a long time ago, especially on the internet. My opinions may have changed since then. Technological progress may have made this information completely obsolete. Proceed with caution.

I recently came across a clever way of writing preprocessor macros, and I figured that I would share.

Let’s say that for some reason you need to write a macro: MACRO(X,Y)1. You want this macro to emulate a function call in every way2.

Example 1: This should work as expected.

if (x > y)
  MACRO(x, y);
do_something();

Example 2: This should not result in a compiler error.

if (x > y)
  MACRO(x, y);
else
  MACRO(y - x, x - y);

Example 3: This should not compile.

do_something();
MACRO(x, y)
do_something();

The naïve way to write the macro is like this:

#define MACRO(X,Y)                       
cout << "1st arg is:" << (X) << endl;    
cout << "2nd arg is:" << (Y) << endl;    
cout << "Sum is:" << ((X)+(Y)) << endl;

This is a very bad solution which fails all three examples, and I shouldn’t need to explain why.

Now, the way I most often see macros written is to enclose them in curly braces, like this:

#define MACRO(X,Y)                         
{                                          
  cout << "1st arg is:" << (X) << endl;    
  cout << "2nd arg is:" << (Y) << endl;    
  cout << "Sum is:" << ((X)+(Y)) << endl;  
}

This solves example 1, because the macro is in one statement block. But example 2 is broken because we put a semicolon after the call to the macro. This makes the compiler think the semicolon is a statement by itself, which means the else statement doesn’t correspond to any if statement! And lastly, example 3 compiles OK, even though there is no semicolon, because a code block doesn’t need a semicolon.

The solution is kind of clever, I thought:

#define MACRO(X,Y)                         
do {                                       
  cout << "1st arg is:" << (X) << endl;    
  cout << "2nd arg is:" << (Y) << endl;    
  cout << "Sum is:" << ((X)+(Y)) << endl;  
} while (0)

Now you have a single block-level statement, which must be followed by a semicolon. This behaves as expected and desired in all three examples. I have noticed this macro pattern before, but I never really thought about why it was written this way. Mainly because I don’t often write macros to begin with.


  1. You should first ask yourself why you can’t just write a regular function and declare it inline, so that the compiler will do the work for you. I’m going to assume there is some good reason why you must use a macro. 

  2. Every way, that is, except that it can’t return a value. That gets much trickier and involves heavy abuse of the ?: operator, if it is even possible at all. 


Lunar eclipse of aught-eight

Warning: I wrote this blog in 2008. That is a long time ago, especially on the internet. My opinions may have changed since then. Technological progress may have made this information completely obsolete. Proceed with caution.

Lunar eclipse, 45 minutes prior to totality

While looking for information on yesterday’s lunar eclipse, I found NASA’s solar eclipse website which has maps of every solar eclipse from 2000 BC to 3000 AD in 20-year blocks. I took a look through these, it seems like I only get three chances to see a solar eclipse in my lifetime, assuming: 1) I live in this general area of the planet my whole life; 2) I don’t want to travel more than 5-6 hours to see one; and 3) I live to be at least 96 years old.

The first opportunity has already passed by: a partial solar eclipse on May 30, 1984 which passed right over my hometown. I don’t remember it; I guess I was too two to care.1

However, on Monday, August 21, 2017, a total solar eclipse will pass by very close to where I currently live. I’m thinking on that day I’ll take the day off work and head out to somewhere in the western North Carolina mountains to see the eclipse. If anyone wants to join me then go ahead and mark your calendar.

Path of August 21, 2017 total solar eclipse

The third opportunity for me to see an eclipse will be May 11, 2078. I will be 96.5 years old then, so I’m not sure if I’ll still care (assuming, of course, that I’m still alive, which is statistically improbable).

One last thing that I couldn’t think of a way to segue into: there is an interesting story about how Christopher Columbus used a lunar eclipse to save his life. Sufficiently advanced technology is indistinguishable from magic.


  1. I seem to recall an eclipse happening when I was in middle school. I know it didn’t get dark or anything, but I think it got a little bit dimmer outside. This must have been the May 10, 1994 partial solar eclipse, although the path of the eclipse was several hundred miles from North Carolina. 


Array-casting in Java

Warning: I wrote this blog in 2007. That is a long time ago, especially on the internet. My opinions may have changed since then. Technological progress may have made this information completely obsolete. Proceed with caution.

Since I haven’t posted anything this week, I figured I’d share something annoying I discovered in Java: you can’t assume that you can put an object of type T into a T array (unless you happen to know that T is declared as a final class).

Take for example this code, which tries to put an Integer (which is an Object) into an array of Objects:

public static void main(String[] args)
{
  Object[] objects = new String[2];
  objects[0] = "ABC";
  objects[1] = new Integer(5);
}

This code compiles with no problem but when run it gives a runtime error on the objects[1]= line. But if the array were declared as new Object[2]; it would run with no complaints.

The problem is that you’re allowed to cast an array of type T to an array of a super-type of T, but you don’t really have an array of the super-type. I imagine they decided to allow this because of the usefulness of casting arrays to super-types for reading the data. But it opens up a whole new set of bugs that most of the time you wouldn’t even think to check for (especially if the array is declared in someone else’s code).

Apparently C# has controversially included the same feature.


Frigidamanus supermus

Warning: I wrote this blog in 2007. That is a long time ago, especially on the internet. My opinions may have changed since then. Technological progress may have made this information completely obsolete. Proceed with caution.

Though it may not be recognized by the American Medical Association (yet!), frigidamanus supermus is a serious medical affliction which is estimated to affect over two hundred thousand Americans every year. It is common among individuals who spend much of their time operating computers; particularly vulnerable are employees at workplaces that maintain a low temperature due to an abundance of heat-generating computer hardware.

Deriving from the Latin for “cold hand above mouse,” the condition is marked by a significant temperature gradient between the hand which operates the mouse (typically the right hand) and the other hand, with the mouse-operating hand being colder. This is caused by the tendency of the computer operator to use the mouse more frequently than the keyboard, while keeping the other hand close to the warmth of the face and/or abdomen.

The outlook is grim: a staggering 98 percent of FS sufferers will survive for no more than 85 years after symptoms are first experienced.


Java overload

Warning: I wrote this blog in 2007. That is a long time ago, especially on the internet. My opinions may have changed since then. Technological progress may have made this information completely obsolete. Proceed with caution.

Something I learned today: overloaded method resolution in Java is done at compile-time, not runtime. Warning: if you have no idea what half the words in that sentence meant, then you probably don’t care about the rest of this post.

Let’s say Alice (who is in tons of hacking books for some reason) wrote this code:

public class Alice
{
  public static long roundToNearestFive(long n)
  {
    return Math.round(n / 5.0) * 5L;
  }
}

And let’s say Bob (who totally has a thing for Alice even though she says they are just friends) wrote this code:

public class Bob
{
  public static void main(String[] args)
  {
    System.out.println(Alice.roundToNearestFive(12.7);
  }
}

When Bob runs his code it will print 10, since 12.7 will be silently converted to an integer (12) to be passed into roundToNearestFive(), and 12 is closer to 10 than to 15. Bob could call roundToNearestFive( Math.round(12.7)) to fix this, but that is annoying because now he has to first round his floating-point numbers before passing them into a rounding function. So Bob asks Alice to provide a fix, and she adds a version of the function which takes a floating-point number:

public class Alice
{
  public static long roundToNearestFive(long n)
  {
    return Math.round(n / 5.0) * 5L;
  }
  public static long roundToNearestFive(double d)
  {
    return Math.round(d / 5.0) * 5L;
  }
}

She sends a new .jar file to Bob with the change, and he runs his code again, expecting it to now output 15. But it still prints 10.

The problem is that when Bob compiled his code, there was no roundToNearestFive(double) function available. So the compiler generated bytecode that looked something like Parent.roundToNearestFive( (long)12.7). So even when he runs with Alice’s new code in place, the bytecode is still forced to call the integer version of the function. The only solution for Bob is to recompile his code against the new .jar file sent from Alice.

For further reference, here is the spec for binary compatibility in Java. And here is more information about Alice and Bob.