My Octopress Blog

A blogging framework for hackers.

Boost, Typedef, #define and GCC Pain

Recently I’ve been working on porting some code to Mac, and yesterday I ran into a bug that stumped me for a little bit. Compiling against Boost was raising a bunch of errors, specifically in lines that seemed pretty innocuous (from cstdint.hpp):

1
2
3
4
5
6
  using ::int8_t;
  using ::int_least8_t;
  using ::int_fast8_t;
  using ::uint8_t;
  using ::uint_least8_t;
  using ::uint_fast8_t;

G++ kept giving me errors for each of those lines: error: expected unqualified-id before ‘signed’, referring to the line using ::int8_t. I’m a little embarrassed that I couldn’t figure it out right away, but eventually I figured out that it was caused by int8_t being #define‘d somewhere else. For those of you that don’t know, #define really just takes one term and substitutes every subsequent occurrence of that term with one you provide.

1
2
3
4
5
// If you define it as a macro
#define int8_t signed char
// Then this line will be interpreted very differently from how you expect
using ::int8_t;
// Gets interpreted as "using ::signed char"!!!

And this is what g++ had been complaining about. That is not legal C++ syntax; I’m sorry I doubted you, g++! But there still remained a larger question: where were these types getting defined? I didn’t want to be in the business of “patching” a library, and especially the largely impeccable Boost library. Typically these types (int8_t, uint8_t, etc.) are defined in cstdint or stdint.h, but looking at the system’s version, I found to my surprise that they were not macro-defined, but typedef’d, which is the right way to do it.

Sidebar: In general, you should be using typedef instead of #define for this reason, and another very good reason. Because #define macros just go through code and blindly replace references, it can be difficult to trace the origin of a type, but typedefs are carried through, and so even after preprocessing, you can still see what the semantic meaning was (that you wanted int8_t specifically, not just something that happens to be the same type). And when debugging, this extra type information can be helpful. Similarly, you should generally also use const to define constants in your code, instead of #define macros, because while you might remember what a magic number is when you write it into your library, the meaning of that particular constant becomes unclear when you encounter it’s value when debugging. (If you haven’t, read Scott Meyer’s Effective C++.)

Getting back to the morality tale, the library I was porting wasn’t macro-defining int8_t, and stdint.h wasn’t, so where was the culprit? Clearly there are potentially hundreds of places it could be, and I was running out of good guesses. Luckily, SEOmoz C++ shaman Martin taught me a little ninja magic: use the -E flag with g++ to only run the preprocessor stage, and redirect the output into a file! When compiling with ‘make,’ it typically spits out the offending g++ command, so rerun it to just pass that one through the preprocessor, which reaches out and fetches the header files and gloms them in order into one giant input file. Then, step through this file to see where “define” and int8_t occur on the same line! In two minutes we were able to find the header that was causing all this trouble, where I had spent two hours learning and reading about where the problem might be.

In the end we found it in a very small library that we happened to use, and on Mac we had just been using a slightly old version and this problem had been fixed in subsequent releases. Still, I’m glad to have added this preprocessor trick to my toolkit.