Broad Network


C++ Macro

C++ Preprocessing Directives – Part 1

Forward: In this part of the series, I talk about C++ Macro.

By: Chrysanthus Date Published: 18 Sep 2012

Introduction

This is part 1 of my series, C++ Preprocessor Directives. In this part of the series, I talk about C++ Macro. The C++ macro is a constant value assignment or a very basic function. What you can do with C++ macro can be done with normal C++ programming. However, a macro has the advantage that you do not have to specify the return value type. It has the disadvantage that it takes longer to be executed. Macros are created using what is known as the #define directive.

C++ has two main stages of compilation. The first stage is some pre-compilation called, Preprocessing. The second stage is the real compilation. Everything taught in this series is preprocessed (pre-compiled); that is, everything taught in this series is for the pre-compilation stage.

The #define Directive
The #define directive is used to define a constant value or a very basic function. Its construct is called a macro. To use the #define directive, you type # at the beginning of a line. This is followed by the word, “define” without the quotes, then an identifier, then a space and then the value or function definition. There is no assignment operator and the construct does not end with a semicolon.

Constant Value
Read and try the following code that works:

#include <iostream>
using namespace std;

#define myVal 33

#define yourVal 14.78

int main()
    {
        cout << myVal << "\n";
        cout << yourVal;

        return 0;
    }

Note that the first define directive instruction defines an int, while the second one defines a float. The value for each such identifier cannot be changed (or identifier re-assigned to a different value). The identifiers are identifying constant values. You cannot redefine such an identifier for a new value below in the code, because the value is constant in relation to the identifier.

String Constant
In a similar way you can have a string constant. The following program illustrates this:

#include <iostream>
using namespace std;

#define str "I am some text in double quotes."

int main()
    {
        cout << str;

        return 0;
    }

Remember, no semicolon after the preprocessing directive, be it for a number or for a string. The preprocessing directive is the whole line beginning from "#define" till the keyboard Enter key (newline character) is pressed. Above, str is the identifier and "I am some text in double quotes." is the value.

The undef Directive
Since we are dealing with a constant, in order to use the same identifier again for whatever reason, you must first cancel the current definition by undefining it using the #undef preprocessor directive. The following program illustrates this:

#include <iostream>
using namespace std;

#define yourVal 14.78

#undef yourVal

#define yourVal 2

int main()
    {
        cout << yourVal;

        return 0;
    }

In this code, the identifier, yourVal, was first defined as 14.78, then undefined, and then redefined as 2. Note the change in value types. In normal C++ coding, you cannot reassign a value to an identifier of constancy, everything being equal; that is what you should expect. However, here, you have the choice to cancel the definition and then redefine with a new value. However, avoid taking advantage of this; it is not good practice.

All preprocessing directives including #undef do not need the semicolon at the end of the construct (one line construct).

Scope of the #define Directive
The value of the #defined directive can be used until it is undefined with #undef. The following program illustrates this:

#include <iostream>
using namespace std;

#define yourVal 14.78

float myFloat = yourVal;

#undef yourVal

int main()
    {
        cout << myFloat;

        return 0;
    }

Note that between the #define and the #undef is a statement using the identifier of yourVal. You can have many statements in that gap.

Function-Like Macro
When you define a constant with the #define directive, as illustrated above, the construct is a macro. You can also have macros that behave like functions. The use of such macros is for very basic functions that are commonly used by an application (large program) in different parts of the application. Such a macro can still be used by a small program. Such a macro is best kept in a library.

A function-like macro or simply, function macro, has the advantage that it can take parameters without declaration. However, the parameters should be appropriate (see below). The function macro does not also take a return type specifier.

The following is an example definition of a macro-like function (copied from the specification):

#define max(a, b) ((a) > (b) ? (a) : (b))

The function-like macro begins with the #define directive. Then you have a space and then the macro name. After that you have the parameter parentheses. Inside the parentheses you have parameters without parameter (object) types; that is, without declaration. The function block is not delimited by curly brackets. It is delimited by parentheses. Each parameter name in the function block is optionally in parentheses. The above function block uses the contracted if-construct (conditional operator), which I explained to you in the series I wrote titled, C++ Operators. Note that the function-like macro does not end with a semicolon. The funtion macro is on one programming line, meaning that you press the Enter key only when you have finished typing the macro. All macros, including the constant macros above, must be on one programming line. The above function returns the maximum of 2 numbers that are compatible.

You call a function-like macro in the same way that you call an ordinary function. The following program shows the above function-like macro in action:

#include <iostream>
using namespace std;

#define max(a, b) ((a) > (b) ? (a) : (b))

int main()
    {

        float myFlt = max(5, 5.3);

        cout << myFlt;

        return 0;
    }


Consider the following 2 macros:

#define myNum 2

#define add(no) (no + myNum + 6)

The first one simply defines the number 2. The second one is a function-like macro. The second one demonstates that inside the function block, the use of parentheses around a parameter name is optional. It also demonstrates that you can use the identifier (e.g. myNum) of a simple macro inside the function block of a function macro. It also demonstrates that you can use number literals (e.g. 6) inside the block. It also demonstatrates that the result of the block is returned and you do not need the "return" reserved word (instruction). The following program puts the above two macors in action:

#include <iostream>
using namespace std;

#define myNum 2

#define add(no) (no + myNum + 6)

int main()
    {

        float myFlt = add(3.4);

        cout << myFlt;

        return 0;
    }

Try it.

For a function-like macro, the function block can be preceded by an instruction as in the following case:

#define add(no) cout << (no + myNum + 6)

The instruction preceding the block here is "cout <<". The following program puts this in action:

#include <iostream>
using namespace std;

#define myNum 2

#define add(no) cout << (no + myNum + 6)

int main()
    {
        add(3.4);

        return 0;
    }

Try it and not that the addition result is printed.

Points to remember about the Function-like Macro
- A function macro goes on one programming line with the keyboard Enter key pressed only after all the macro has been typed. Even if the macro is seen on more than one line, the Enter key (newline character) is pressed only after all the description of the macro (at the end).
- It begins with the preprocessing instruction, "#define" at the beginning of a line, followed by a space, and then the name of the macro, then parameters in parentheses, then optional instruction, then the function block in parentheses.
- Inside the function block, a parameter name is optionally in parentheses; number literals can be used and simple macro identifiers can be used.
- The object types (number identifier types) for the macro are not pre-declared. That is you do not have to pre-declare an object or number as an int or float, etc.
- A function macro ends with the press of the Enter key and not a semicolon.
- The result of the function block is returned, without use of the return instruction.

That is what I have for you on macros. We stop hear and continue in the next part of the series.

Chrys

Related Links

C++ Taking the Bull by the Horns
C++ Just After the Basics
Object Oriented Programming in C++
C++ Operators
C++ Templates
Exception Handling in C++
Container Library Sequences in C++ Simplified
Associative Container in C++ Simplified
String in C++ Standard Library Simplified
Date and Time in C++ Simplified
Advanced Course
Storage Duration in C++
Scopes in C++
Function and Operator Overloading in C++
Specifiers in C++
Some Features of C++ Entities
C++ Preprocessing Directives
Writing a C++ Container
C++ Namespace
Writing a C++ Application
More Related Links
Relational Database and Sybase
Windows User Interface
Computer Programmer – A Jack of all Trade – Poem
C++ Course
NEXT

Comments

Become the Writer's Fan
Send the Writer a Message