BogoToBogo
  • Home
  • About
  • Big Data
  • Machine Learning
  • AngularJS
  • Python
  • C++
  • go
  • DevOps
  • Kubernetes
  • Algorithms
  • More...
    • Qt 5
    • Linux
    • FFmpeg
    • Matlab
    • Django 1.8
    • Ruby On Rails
    • HTML5 & CSS

C++ Tutorial - Macro - 2020

cplusplus_icon.png




Bookmark and Share





bogotobogo.com site search:




Introduction

When the C++ compiler runs, it first calls preprocessor to find any compiler directives that may be included in the source code.


The directives begin with the # character and will be implemented first to modify the source code before it is assembled and compiled.

The changes made by compiler directives to the preprocessor create new temporary files that are not normally seen. It is these temporary files that are used to create a binary object file.

The compilation process employs the Preprocessor to compile source code, an Assembler to translate this into machine code, and a Linker to convert one or more binary objects into an executable program.



compiler_pipes

So, the preprocessor will replace

#include <iostream>
directive with definitions for the cin, cout, cerr functions.



#define

In the previous section, we saw the preprocessor substitutes library code for #include directives. Other preprocessor directives can be used to substitute test or numerics before assembly and compilation.

The #define directives specifies a macro, comprising an identifier name and a string or numerics, to be substituted by the preprocessor for each occurance of that macro in the source code.

#define STARS "*****"
#define SITE "bogotobogo.com"
#define YEAR 2011

#include <iostream>
using namespace std ;

int main()
{
	cout << STARS << endl << SITE << endl << STARS ;
	cout << endl << "YEAR is: " << YEAR ;
	cout << endl << "Next YEAR is: " << ((YEAR)+1) << endl ;

	return 0 ;
}

Output is:

*****
bogotobogo.com
*****
YEAR is: 2011
Next YEAR is: 2012

Similar to #include preprocessor directives, #define directives can appear at the start of the source code. As with constant variable names, the macro name uses uppercase, and defined string values should be encolsed within double quotes. For numeric substitution in expression the macro name should be enclosed in parentheses to ensure correct precedence.

Avoid #define for constant

There are couple of reasons for avoiding #define for constant:

  1. No type checking
    For the constant we are defining using #define, there are no provisions of type checking. So, we should make sure that we explicitly specify the type of the constant, such as 3.4f.
  2. No scoping
    A constant defined is global.
  3. No access control
    We cannot impose any access control over #define, and it's always public.
  4. No symbol
    In the previous example, we used YEAR constant, but this symbolic name may be stripped from our code by the preprocessor. As a result, the compiler never sees the name and cannot put it into symbol table. This causes debugging headache when we can just see the value of the constant not the name.


#ifdef, #ifndef, #if defined, #else, #elif defined

The #ifdef directive performs the most common preprocessor function by testing to see if a specified macro has been defined. When the macro has been defined, so the test returns true, the preprocessor will do insert on subsequent lines up to a corresponding #endif directive.

On the other hand, the #ifndef directive tests to see if a specified macro has not been defined. When that test returns true, it will do insert up the a corresponding #endif directive.

Any previously defined macro can be removed later using the #undef directive so that subsequent #ifdef conditional tests fail. The macro can then be redefined by using #define directive again.

To test multiple definitions, the #ifdef macro can be expressed as #if defined and additional tests made #elif defined macros.

#if defined _WIN32
	#define PLATFORM "Windows"
#elif defined __linux
	#define PLATFORM "Linux"
#elif defined __macosx
	#define PLATFORM "MacOSX"
#else
	#define PLATFORM "Others"
#endif

A C++ code usually has many .h header files, and the header files may contain one or more #include directives to make other classes or functions available from other header files. This can cause duplication where definitions appear in two files. The solution to this problem of redefinition is to use preprocessor directives to ensure the compiler will only be exposed to a single definition, so called inclusion guards. This creates a unique macro name for each header file. The name is an uppercase version of the file name, with the dot changed to an underscore. For instance, myfunction.h is represented as MYFUNCTION_H.

To create a macro to guard against duplication, an #ifndef directive first tests to see if the definition has already been made by another header file included in the same program. If the definition already exists, the compiler ignores the duplicate definition, otherwise, a #define directive will permit the compiler to use the definition in that header file.

#ifndef MYFUNCTION_H
	#define MYFUNCTION_H
		inline int func( int x , int y ){ return ( x * y ) ; } 
#endif


API with #ifdef

A well-designed C++ API must always avoid platform-specific #ifdef/#endif lines in its public headers. As an example, let's look at the following API which encapsulates the functionality offered by a tablet. Some tablet offer built-in GPS, but not all of them. But as an API designer, we should never expose this directly through our API:

class TabletDevice
{
public:
    #if defined TARGET_OS_TABLET
        bool getGPS(double &latitude;, double &longitude;);
    #endif
};

The platform specific API design requires a different API on different platforms, which means it also forces the clients of our API to introduce the same platform specificity into their own applications. In other words, our clients should guard any calls go getGPS() with the same #if statement, otherwise their code may fail to compile with an undefined symbol error on other platforms.

Making things worse, if in a later version of the API, we also add support for another device class, like Windows Tablet, then we need to update the #if line in our public header to include something like _WIN32_WCE. Then, our clients also need to find all instances in their code where they have embedded the TARGET_OS_TABLET define and extend it to also include _WIN32_WCE. This is the price we have to pay because we have exposed the implementation details of our API.

Therefore, we should hide the fact that the function only works on certain platforms and provide a method to determine whether the implementation offers the desired functionalities on the current platform:

class TabletDevice
{
public:
    bool hasGPS() const;
    bool getGPS(double &latitude;, double &longitude;);
};

In this way, we now have our API which is consistent over all platforms and it does not expose the details of which platform support GPS. So, the client can write code to check whether the current device supports GPS device, by calling hasGPS(), and if so, they can call the getGPS() method. The method hasGPS() can be something like this:

bool TabletDevice::hasGPS() const
{
public:
    #if defined TARGET_OS_TABLET
        return true;
    #else
        return false;
    #endif
}

This is much better design than the original design because the platform specific #if is now hidden in the .cpp file instead of being exposed in the header file.




Macro Functions

#define can be used to make macro functions that will be substituted in the source before compilation. A preprocessor function declaration comprises a macro name immediately followed by parentheses containing the function's argument. Do not leave any space between the name and the parentheses. The declaration is then followed by the function definition within another set of parentheses. For example, a preprocessor macro function to give bigger value of the two looks like this:

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

When we use macro functions, however, unlike regular functions, they do not perform any kind of type checking. Because of this drawbacks, inline functions are usually preferable to macro functions. But because macros directly substitute their code, they reduce the overhead of a function call.

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

#include <iostream>
using namespace std ;

inline int max(int a, int b) {return (a > b ? a: b);}

int main()
{	
	int x = 10, y = 20;

	cout << "Macro Max(x,y) = " << MAX(x,y) << endl;
	cout << "inline max(x,y) = " << max(x,y) << endl;

	return 0;
}

Output is:

Macro Max(x,y) = 20
inline max(x,y) = 20

One of the common mistakes we make when we use Macro is to forget what Macro is suppose to do. In the following example, if we miss parenthesis around it, it will give us unexpected result.

#include <stdio.h>
#define SQUARE(n) ((n)*(n))
int main()
{
	int j = 64/SQUARE(4);
	printf("j = %d",j);

	return 0;
}

surprisingly, it prints out j = 64 instead of j = 4.
Why?
Because j = 64/4*4 but not j = 64/(4*4).

So, we need to use the following Macro to get intended answer.

#define SQUARE(n) (n*n)

Here is another example which may give unexpected results:

#include <stdio.h>
#define SQR(n)(n*n)

int main()
{
    int a, b = 3;
    a = SQR(b+2);        // a = (b+2*b+2) = 3+2*3+2 = 11 not 25
    printf("%d\n", a);  
    return 0;
}

So, in this case, the macro should be:

#define SQR(n)((n)*(n))


Stringizing Operator (#)

The number-sign or stringizing operator (#) converts macro parameters to string literals without expanding the parameter definition. It is used only with macros that take arguments. In other words, it converts a characters passed as a macro argument into a string, and adds double quotes to enclose the string.

All whitespaces before or after the characters passed as a macro argument to the stringizing operator (#) is ignored and multiple spaces between characters is reduced to just one space.

The stringizing operator is useful to pass string values to a preprocessor #define directive without needing to surround each string with double quote.

When we use the merging operator (##), we can combine two terms into a single term.

#define SIMPLE(s) cout << #s << endl
#define MERGE( s1, s2 ) cout << s1##s2 << endl 

#include <string>
#include <iostream>
using namespace std ;

int main()
{
	string anotherline = "A host of dancing " ;
	anotherline += "Daffodils; " ;

	SIMPLE(I wandered lonely as a Cloud) ;
	SIMPLE(That floats on high oer Vales and Hills) ;
	SIMPLE(When all at once              I saw a crowd) ;
	MERGE(another, line ) ;
	SIMPLE(On fishing up the moon.) ;
	SIMPLE(Along the Lake beneath the trees);
        SIMPLE(Ten thousand dancing in the breeze.);

	return 0 ;
}

Output is:

I wandered lonely as a Cloud
That floats on high oer Vales and Hills
When all at once I saw a crowd
A host of dancing Daffodils;
On fishing up the moon.
Along the Lake beneath the trees
Ten thousand dancing in the breeze.





ASSERT

By defining an ASSERT macro function, we can evaluate a specified condition for a bollean value. The condition to be evaluated will be passed from the caller as the ASSERT function argument. The function can then excute appropriate statements according to the result of the evaluation. Multiple statements can be included in the macro function definition by adding a backslash at the end of each line, allowing the definition to continue on the next line.

Several statements calling the ASSERT function can be added to the code to monitor the a condition as the program proceeds. An ASSERT can be controlled by a DEBUG macro, which allows debugging to be easily turned on and off simply by changing the value of the DEBUG control macro.

#define DEBUG 1

#if( DEBUG == 1 )
#define ASSERT( expression ) 				        \
	cout << #expression << " ..." << num ;		        \
	if( expression!= true) {				\
  		cout << " Failed in file: " << __FILE__ ;       \
  		cout << " at line: " << __LINE__ << endl;	\
	}							\
	else cout << " Passed" << endl ; 	
#elif( DEBUG == 0 )
#define ASSERT( result )					\
cout << "Number is " << num << endl ;
#endif 


#include <iostream>
using namespace std ;

int main()
{
	int num = 99 ;

	ASSERT( num < 100 ) ;

	num++ ;

	ASSERT( num < 100 ) ; 

	return 0 ;
}

We started the program by defining a DEBUG macro with an ON value of 1 to control the ASSERT function"

#define DEBUG 1

Then, we add a macro if-elif to define the ASSERT according to the control value

Output is:

num < 100 ...99 Succeeds
num < 100 ...100 Fails in file: c:\assrt.cpp at line: 31



SeoRakSanSoGongRyongNeungSun




Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization

YouTubeMy YouTube channel

Sponsor Open Source development activities and free contents for everyone.

Thank you.

- K Hong






Sponsor Open Source development activities and free contents for everyone.

Thank you.

- K Hong






C++ Tutorials

C++ Home

Algorithms & Data Structures in C++ ...

Application (UI) - using Windows Forms (Visual Studio 2013/2012)

auto_ptr

Binary Tree Example Code

Blackjack with Qt

Boost - shared_ptr, weak_ptr, mpl, lambda, etc.

Boost.Asio (Socket Programming - Asynchronous TCP/IP)...

Classes and Structs

Constructor

C++11(C++0x): rvalue references, move constructor, and lambda, etc.

C++ API Testing

C++ Keywords - const, volatile, etc.

Debugging Crash & Memory Leak

Design Patterns in C++ ...

Dynamic Cast Operator

Eclipse CDT / JNI (Java Native Interface) / MinGW

Embedded Systems Programming I - Introduction

Embedded Systems Programming II - gcc ARM Toolchain and Simple Code on Ubuntu and Fedora

Embedded Systems Programming III - Eclipse CDT Plugin for gcc ARM Toolchain

Exceptions

Friend Functions and Friend Classes

fstream: input & output

Function Overloading

Functors (Function Objects) I - Introduction

Functors (Function Objects) II - Converting function to functor

Functors (Function Objects) - General



Git and GitHub Express...

GTest (Google Unit Test) with Visual Studio 2012

Inheritance & Virtual Inheritance (multiple inheritance)

Libraries - Static, Shared (Dynamic)

Linked List Basics

Linked List Examples

make & CMake

make (gnu)

Memory Allocation

Multi-Threaded Programming - Terminology - Semaphore, Mutex, Priority Inversion etc.

Multi-Threaded Programming II - Native Thread for Win32 (A)

Multi-Threaded Programming II - Native Thread for Win32 (B)

Multi-Threaded Programming II - Native Thread for Win32 (C)

Multi-Threaded Programming II - C++ Thread for Win32

Multi-Threaded Programming III - C/C++ Class Thread for Pthreads

MultiThreading/Parallel Programming - IPC

Multi-Threaded Programming with C++11 Part A (start, join(), detach(), and ownership)

Multi-Threaded Programming with C++11 Part B (Sharing Data - mutex, and race conditions, and deadlock)

Multithread Debugging

Object Returning

Object Slicing and Virtual Table

OpenCV with C++

Operator Overloading I

Operator Overloading II - self assignment

Pass by Value vs. Pass by Reference

Pointers

Pointers II - void pointers & arrays

Pointers III - pointer to function & multi-dimensional arrays

Preprocessor - Macro

Private Inheritance

Python & C++ with SIP

(Pseudo)-random numbers in C++

References for Built-in Types

Socket - Server & Client

Socket - Server & Client 2

Socket - Server & Client 3

Socket - Server & Client with Qt (Asynchronous / Multithreading / ThreadPool etc.)

Stack Unwinding

Standard Template Library (STL) I - Vector & List

Standard Template Library (STL) II - Maps

Standard Template Library (STL) II - unordered_map

Standard Template Library (STL) II - Sets

Standard Template Library (STL) III - Iterators

Standard Template Library (STL) IV - Algorithms

Standard Template Library (STL) V - Function Objects

Static Variables and Static Class Members

String

String II - sstream etc.

Taste of Assembly

Templates

Template Specialization

Template Specialization - Traits

Template Implementation & Compiler (.h or .cpp?)

The this Pointer

Type Cast Operators

Upcasting and Downcasting

Virtual Destructor & boost::shared_ptr

Virtual Functions



Programming Questions and Solutions ↓

Strings and Arrays

Linked List

Recursion

Bit Manipulation

Small Programs (string, memory functions etc.)

Math & Probability

Multithreading

140 Questions by Google



Qt 5 EXPRESS...

Win32 DLL ...

Articles On C++

What's new in C++11...

C++11 Threads EXPRESS...

Go Tutorial

OpenCV...








Contact

BogoToBogo
contactus@bogotobogo.com

Follow Bogotobogo

About Us

contactus@bogotobogo.com

YouTubeMy YouTube channel
Pacific Ave, San Francisco, CA 94115

Pacific Ave, San Francisco, CA 94115

Copyright © 2024, bogotobogo
Design: Web Master