C++ by Example
C++ by Example

Preprocessor and Macros

2 min read

Preprocessor and Macros

The C++ preprocessor runs before the compiler. It performs textual substitution, conditional compilation, and file inclusion. Its output is valid C++ source that the compiler then translates.

#include

Copies a file into the current translation unit:

#include <vector>       // standard library header — searches include path
#include "myheader.h"   // project header — searches relative to current file

Include guards

Without protection, including a header twice in one translation unit causes duplicate declarations. Include guards prevent this:

// myclass.h
#ifndef MYCLASS_H
#define MYCLASS_H

class MyClass {
    // ...
};

#endif

#pragma once is a non-standard but universally supported alternative that is shorter and harder to get wrong:

#pragma once

class MyClass {
    // ...
};

#define

Defines a macro — a textual substitution:

#define MAX_CONNECTIONS 100
#define SQUARE(x) ((x) * (x))

int n = MAX_CONNECTIONS;   // replaced with 100
int s = SQUARE(3 + 1);     // replaced with ((3 + 1) * (3 + 1)) = 16

The extra parentheses in SQUARE(x) are essential. Without them, SQUARE(3 + 1) expands to 3 + 1 * 3 + 1 = 7.

In modern C++, prefer constexpr for constants and inline functions (or templates) for function macros — they are type-safe and debuggable.

Conditional compilation

#define DEBUG

#ifdef DEBUG
    std::cout << "debug mode\n";
#endif

#ifndef NDEBUG
    // assertions are active
#endif

#if defined(__linux__)
    // Linux-specific code
#elif defined(_WIN32)
    // Windows-specific code
#else
    // everything else
#endif

NDEBUG is the standard macro that disables assert(). It is defined by many build systems in release mode.

assert

#include <cassert>

int divide(int a, int b) {
    assert(b != 0);   // aborts with message if false; disabled when NDEBUG is defined
    return a / b;
}

Use assert for internal programmer errors — preconditions that should never be false. For recoverable errors, use exceptions or std::expected.

Predefined macros

Macro Value
__FILE__ Current source file name
__LINE__ Current line number
__func__ Current function name
__DATE__ Compilation date
__TIME__ Compilation time
__cplusplus C++ standard version (e.g., 201703L for C++17)

Useful for logging and debugging:

std::cout << "Error at " << __FILE__ << ":" << __LINE__ << "\n";

#pragma

#pragma is implementation-defined. Common uses:

#pragma once              // include guard
#pragma GCC optimize("O3") // optimizer hint (GCC)
#pragma warning(disable: 4996) // suppress warning (MSVC)

Avoid relying on #pragma for portability-critical code.

Stringification and token pasting

#define STRINGIFY(x) #x
#define CONCAT(a, b) a##b

std::cout << STRINGIFY(hello world) << "\n";   // "hello world"

int CONCAT(my_, var) = 10;   // int my_var = 10;

These are advanced macro features occasionally used in logging frameworks and code generators.

C++ by Example
C++ by Example

Learn modern C++ through working code. Each chapter introduces one concept — variables, functions, classes, templates, smart pointers, concurrency — with a clear example, a line-by-line explanation, and notes on how it applies in real programs.

View book →