Flylib.com

Books Software

 
 
 

10.5 Advanced Features

I l @ ve RuBoard

10.5 Advanced Features

This book does not cover the complete list of C++ preprocessor directives. Among the more advanced features are an advanced form of the #if directive for conditional compilations and the #pragma directive for inserting compiler-dependent commands into a file. See your C++ reference manual for more information on these features.

I l @ ve RuBoard
I l @ ve RuBoard

10.6 Summary

The C++ preprocessor is a very useful part of the C++ language. It has a completely different look and feel from C++. However, it must be treated apart from the main C++ compiler.

Problems in macro definitions often do not show up where the macro is defined, but result in errors much further down in the program. By following a few simple rules, you can decrease the chances of having problems:

  • Put parentheses around everything. In particular they should enclose #define constants and macro parameters.

  • When defining a macro with more than one statement, enclose the code in { }.

  • The preprocessor is not C++. Don't use = or ;.

    #define X = 5 // Illegal
    #define X 5;  // Illegal
    #define X = 5; // Very illegal
    #define X 5   // Correct
    

Finally, if you got this far, be glad that the worst is over.

I l @ ve RuBoard
I l @ ve RuBoard

10.7 Programming Exercises

Note that the solutions to all the exercises below can be obtained using standard C++ syntax such as inline and enum . In general, using C++ construction is preferred over using macro definitions. However since this is the chapter on the preprocessor, macros should be used for these exercises.

Exercise 10-1: Create a set of macros to define a type called RETURN_STATUS and the following values: RETURN_SUCCESS , RETURN_WARNING , and RETURN_ERROR . Define a macro, CHECK_RETURN_FATAL,,, that takes a RETURN_STATUS as its argument and returns true if you have a fatal error.

Exercise 10-2: Write a macro that returns true if its parameter is divisible by 10 and false otherwise .

Exercise 10-3: Write a macro is_digit that returns true if its argument is a decimal digit. Write a second macro is_hex that returns true if its argument is a hex digit (0-9, A-F, a-f). The second macro should reference the first.

Exercise 10-4: Write a preprocessor macro that swaps two integers. (If you're a real hacker, write one that does not use a temporary variable declared outside the macro.)

I l @ ve RuBoard
I l @ ve RuBoard

10.8 Answers to Chapter Questions

Answer 10-1: After the program has been run through the preprocessor, the std::cout statement is expanded to look like:

std::cout << "The square of all the parts is " <<  7 + 5 * 7 + 5  << '\n';

The equation 7 + 5 * 7 + 5 evaluates to 47. It is a good rule to put parentheses ( ) around all expressions in macros. If you change the definition of ALL_PARTS to:

#define ALL_PARTS (FIRST_PART + LAST_PART)

the program executes correctly.

Answer 10-2: The preprocessor is a very simple-minded program. When it defines a macro, everything past the identifier is part of the macro. In this case, the definition of MAX is literally =10 . When the for statement is expanded, the result is:

for (counter==10; counter > 0; --counter)

C++ allows you to compute a result and throw it away. For this statement, the program checks to see whether counter is 10 and discards the answer. Removing the = from the macro definition will correct the problem.

Answer 10-3: As with the previous problem, the preprocessor does not respect C++ syntax conventions. In this case, the programmer used a semicolon to end the statement, but the preprocessor included it as part of the definition for size . The assignment statement for size , expanded, is:

size = 10; -2;;

The two semicolons at the end do not hurt anything, but the one in the middle is a killer. This line tells C++ to do two things: assign 10 to size and compute the value -2 and throw it away (this results in the null effect warning). Removing the semicolons will fix the problem.

Answer 10-4: The output of the preprocessor looks like:

int main(  ) {      
    int value;    
     
    value = 1;  
    if (value < 0)  
        std::cout << "Fatal Error: Abort\n"; exit(8);  

    std::cout << "We did not die\n"; 
    return (0); 
}

The problem is that two statements follow the if line. Normally they would be put on two lines. If we properly indent this program we get:

Example 10-10. die3/die.cpp
#include <iostream>
#include <cstdlib>

int main(  ) {     
    int value;  // a random value for testing 
    
    value = 1; 
    if (value < 0) 
        std::cout << "Fatal Error:Abort\n";

    exit(8); 

    std::cout << "We did not die\n";
    return (0);
}

From this it is obvious why we always exit. The fact that there were two statements after the if was hidden by using a single preprocessor macro. The cure for this problem is to put curly braces around all multistatement macros.

#define DIE \ 
    {std::cout << "Fatal Error: Abort\n"; exit(8);}

Answer 10-5: The problem is that the preprocessor does not understand C++ syntax. The macro call:

SQR(counter+1)

expands to:

(counter+1 * counter+1)

The result is not the same as ((counter+1) * (counter+1)) . To avoid this problem, use inline functions instead of parameterized macros:

inline int SQR(int x) { return (x*x);}

If you must use parameterized macros, enclose each instance of the parameter in parentheses:

#define SQR(x) ((x) * (x))

Answer 10-6: The only difference between a parameterized macro and one without parameters is the parentheses immediately following the macro name . In this case, a space follows the definition of RECIPROCAL , so it is not a parameterized macro. Instead it is a simple text replacement macro that replaces RECIPROCAL with:

(number) (1.0 / number)

Removing the space between RECIPROCAL and (number) corrects the problem.

I l @ ve RuBoard