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.
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 →