One More Reason the C++ Preprocessor is Dangerous

A co-worker recently asked me to look at an interesting C++ compilation problem. The constructor for one of her classes refused to compile until she simply changed one of the parameter names! Why should the parameter name matter?

Here's a much-simplified version of the class, with a trivial main(), removing proprietary information (this code isn't yet open-source).


#include
#include
#include

class myclass {
public:
   myclass(char \*s_net);
};

myclass::myclass(char \*s_net)
{
   // do stuff
}

int main()
{
   // do stuff
}

The Sun Studio 10 compiler gives the following errors when attempting to compile the translation unit:


"test.cc", line 7: Error: No direct declarator preceding "(".
"test.cc", line 10: Error: myclass is not a static data member.
"test.cc", line 10: Error: Badly formed expression.

Changing the name of the only parameter to the myclass constructor to something else like net allows the code to compile with no errors.

What's going on?

It turns out that s_net is defined in netinet/in.h as follows


#define s_net _S_un._S_un_b.s_b1 /\* OBSOLETE: network \*/

So when the preprocessor makes its substitutions prior to the actual compilation, the symbol s_net is replaced with something like _S_un._S_un_b.s_b1. (There are actually a few more substitutions, as we'll see momentarily). We can see this ourselves by using the -P option to CC to run the test program through just the preprocessor. This gives, in part:


class myclass {
public :
myclass ( char \* S_un . S_un_b . s_b1 ) ;
} ;

myclass :: myclass ( char \* S_un . S_un_b . s_b1 )
{

}

That obviously won't compile!

This example demonstrates two problems. The first is the use of globals, specifically #defines. Who would expect a generic symbol like s_net to be #defined in a standard header file? It would be bad enough if it were simply a global variable, but that at least would probably not have caused a problem in this case, because the use of s_net in the constructor would hide the global s_net. However, the preprocessor actually search/replaces the #define term so that the compiler never gets a chance to see s_net as the parameter name in the constructor.

The second problem is that the preprocessor does this substitution and other modifications such that the code for which the compiler is giving a warning is not the code that you see when you look at your source file. This can make debugging difficult.

Moral of the story: Use the preprocessor judiciously!

Comments:

This isn't just the C++ pre-processor -- this can happen with C code too.

Posted by Nico on August 09, 2007 at 06:56 AM MDT #

Post a Comment:
Comments are closed for this entry.
About

Nick Solter is a software engineer and author living in Colorado.

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today