Docs
Preprocessor Namespaces
Preprocessor Directives & Namespaces in C++
Table of Contents
- •What is the Preprocessor
- •Include Directives
- •Macros
- •Conditional Compilation
- •Pragmas
- •Namespaces
- •Best Practices
What is the Preprocessor
The preprocessor runs BEFORE the compiler. It processes directives (lines starting with #) and transforms your source code.
Compilation Process with Preprocessor
┌─────────────────────────────────────────────────────────────────────────────┐
│ COMPILATION STAGES │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Source Code (.cpp) │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ PREPROCESSOR │ ◄── Handles #include, #define, #ifdef, etc. │
│ │ │ Expands macros, includes headers │
│ └───────┬────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ COMPILER │ ◄── Converts to assembly/object code │
│ └───────┬────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ LINKER │ ◄── Combines object files, resolves symbols │
│ └───────┬────────┘ │
│ │ │
│ ▼ │
│ Executable (.exe, .out) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Include Directives
#include
Copies the contents of a file into your source code.
// System headers (standard library)
#include <iostream> // Searches system include paths
#include <vector>
#include <string>
// User headers (your own files)
#include "myheader.h" // Searches current directory first
#include "utils/helper.h"
// What happens:
// The preprocessor literally copies the entire content
// of the header file into your source file at that point
Include Guards (Prevent Multiple Inclusion)
// myheader.h - Traditional include guard
#ifndef MYHEADER_H // If not defined
#define MYHEADER_H // Define it
class MyClass {
// ...
};
#endif // MYHEADER_H // End of guard
// myheader.h - Modern pragma (preferred)
#pragma once // Simpler, supported by all major compilers
class MyClass {
// ...
};
How Include Guards Work
┌─────────────────────────────────────────────────────────────────────────────┐
│ INCLUDE GUARD MECHANISM │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Without guards: With guards: │
│ │
│ main.cpp: main.cpp: │
│ #include "a.h" → class A {...} #include "a.h" → class A {...} │
│ #include "b.h" → #include "a.h" #include "b.h" → #include "a.h" │
│ → class A {...} ❌ → (skipped) ✓ │
│ DUPLICATE! → class B {...} │
│ → class B {...} │
│ │
│ Error: redefinition of 'class A' Compiles successfully! │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Macros
Object-like Macros (Constants)
#define PI 3.14159265359
#define MAX_SIZE 100
#define APP_NAME "MyApplication"
#define DEBUG_MODE
double area = PI * radius * radius;
int buffer[MAX_SIZE];
Function-like Macros
#define SQUARE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define ABS(x) ((x) < 0 ? -(x) : (x))
int result = SQUARE(5); // Expands to: ((5) * (5))
int bigger = MAX(10, 20); // Expands to: ((10) > (20) ? (10) : (20))
⚠️ Macro Pitfalls
// BAD: Missing parentheses
#define BAD_SQUARE(x) x * x
int a = BAD_SQUARE(2 + 3); // Expands to: 2 + 3 * 2 + 3 = 11 (not 25!)
// GOOD: Proper parentheses
#define GOOD_SQUARE(x) ((x) * (x))
int b = GOOD_SQUARE(2 + 3); // Expands to: ((2 + 3) * (2 + 3)) = 25 ✓
// BAD: Side effects with macros
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int i = 5;
int result = MAX(i++, 3); // i++ evaluated TWICE if i > 3!
// SOLUTION: Use inline functions or constexpr instead
inline int safeMax(int a, int b) { return a > b ? a : b; }
Stringification and Token Pasting
// # - Stringification (convert to string literal)
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
cout << STRINGIFY(Hello World); // Prints: Hello World
cout << TOSTRING(123); // Prints: 123
// ## - Token pasting (concatenate tokens)
#define CONCAT(a, b) a##b
#define MAKE_VAR(n) var##n
int MAKE_VAR(1) = 10; // Creates: int var1 = 10;
int MAKE_VAR(2) = 20; // Creates: int var2 = 20;
int CONCAT(my, Var) = 30; // Creates: int myVar = 30;
Predefined Macros
cout << "File: " << __FILE__ << endl; // Current filename
cout << "Line: " << __LINE__ << endl; // Current line number
cout << "Date: " << __DATE__ << endl; // Compilation date
cout << "Time: " << __TIME__ << endl; // Compilation time
cout << "Function: " << __func__ << endl; // Current function name
// C++ version
#if __cplusplus >= 202002L
cout << "C++20 or later" << endl;
#elif __cplusplus >= 201703L
cout << "C++17" << endl;
#elif __cplusplus >= 201402L
cout << "C++14" << endl;
#elif __cplusplus >= 201103L
cout << "C++11" << endl;
#endif
Undefine Macros
#define TEMP_VALUE 100
// Use TEMP_VALUE...
#undef TEMP_VALUE // Remove the macro definition
// TEMP_VALUE is no longer defined
Conditional Compilation
#if, #elif, #else, #endif
#define DEBUG_LEVEL 2
#if DEBUG_LEVEL == 0
// No debugging
#elif DEBUG_LEVEL == 1
#define LOG(msg) cout << msg << endl
#elif DEBUG_LEVEL >= 2
#define LOG(msg) cout << __FILE__ << ":" << __LINE__ << " " << msg << endl
#else
#define LOG(msg)
#endif
#ifdef, #ifndef
// #ifdef - if defined
#ifdef DEBUG
cout << "Debug mode enabled" << endl;
#endif
// #ifndef - if NOT defined
#ifndef RELEASE
cout << "Not in release mode" << endl;
#endif
// Equivalent to:
#if defined(DEBUG)
// ...
#endif
#if !defined(RELEASE)
// ...
#endif
Platform-Specific Code
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#define PLATFORM "Windows"
#elif defined(__linux__)
#include <unistd.h>
#define PLATFORM "Linux"
#elif defined(__APPLE__)
#include <TargetConditionals.h>
#define PLATFORM "macOS"
#else
#define PLATFORM "Unknown"
#endif
cout << "Running on: " << PLATFORM << endl;
Feature Toggles
// Enable/disable features at compile time
#define ENABLE_LOGGING
#define ENABLE_PROFILING
// #define ENABLE_EXPERIMENTAL // Commented out = disabled
#ifdef ENABLE_LOGGING
void log(const string& msg) {
cout << "[LOG] " << msg << endl;
}
#else
void log(const string& msg) { } // Empty function
#endif
#ifdef ENABLE_PROFILING
#define PROFILE_START auto _start = chrono::high_resolution_clock::now()
#define PROFILE_END cout << chrono::duration<double>(chrono::high_resolution_clock::now() - _start).count() << "s" << endl
#else
#define PROFILE_START
#define PROFILE_END
#endif
Pragmas
Compiler-specific directives:
// Disable specific warning
#pragma warning(disable: 4996) // MSVC
#pragma GCC diagnostic ignored "-Wunused-variable" // GCC/Clang
// Pack structures (control memory alignment)
#pragma pack(push, 1) // 1-byte alignment
struct PackedStruct {
char a; // 1 byte
int b; // 4 bytes
char c; // 1 byte
}; // Total: 6 bytes (not 12 with padding)
#pragma pack(pop)
// Region markers (IDE feature)
#pragma region MySection
// Code here
#pragma endregion
// Include once (alternative to include guards)
#pragma once
// Message during compilation
#pragma message("Compiling with DEBUG enabled")
Namespaces
Namespaces prevent name collisions by organizing code into logical groups.
Namespace Basics
// Define a namespace
namespace Math {
const double PI = 3.14159;
double square(double x) {
return x * x;
}
double cube(double x) {
return x * x * x;
}
}
namespace Physics {
const double PI = 3.14159265359; // More precise PI, no conflict!
double velocity(double distance, double time) {
return distance / time;
}
}
// Using namespace members
int main() {
// Fully qualified names
double area = Math::PI * Math::square(5);
double v = Physics::velocity(100, 10);
return 0;
}
Namespace Visualization
┌─────────────────────────────────────────────────────────────────────────────┐
│ NAMESPACES PREVENT COLLISIONS │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Without namespaces: │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ void print() { ... } // From library A │ │
│ │ void print() { ... } // From library B ❌ ERROR: redefinition │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
│ With namespaces: │
│ ┌────────────────────────┐ ┌────────────────────────┐ │
│ │ namespace LibraryA { │ │ namespace LibraryB { │ │
│ │ void print() {...} │ │ void print() {...} │ │
│ │ } │ │ } │ │
│ └────────────────────────┘ └────────────────────────┘ │
│ │
│ LibraryA::print(); // Calls A's print ✓ │
│ LibraryB::print(); // Calls B's print ✓ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
using Declarations
// using declaration - bring ONE name into scope
using std::cout;
using std::endl;
using std::string;
cout << "Hello" << endl; // No std:: needed
// using directive - bring ALL names from namespace
using namespace std; // Now all std names available
vector<int> v; // No std:: needed
string s; // No std:: needed
⚠️ using namespace std - The Debate
// In .cpp files: Usually OK, especially for small programs
using namespace std;
// In header files: NEVER! Pollutes all files that include the header
// BAD: myheader.h
#pragma once
using namespace std; // ❌ Forces this on everyone who includes!
// GOOD: myheader.h
#pragma once
#include <string>
std::string getName(); // ✓ Explicit, no pollution
// Best practice: Use specific using declarations
using std::cout;
using std::vector;
using std::string;
Nested Namespaces
// Traditional (C++11)
namespace Company {
namespace Product {
namespace Module {
void function() { }
}
}
}
// C++17 simplified syntax
namespace Company::Product::Module {
void function() { }
}
// Usage
Company::Product::Module::function();
// Namespace alias for convenience
namespace CPM = Company::Product::Module;
CPM::function();
Anonymous (Unnamed) Namespaces
// Anonymous namespace - internal linkage (like static)
namespace {
int helperVariable = 42; // Only visible in this file
void helperFunction() { // Only visible in this file
// ...
}
}
// Equivalent to:
static int helperVariable = 42;
static void helperFunction() { }
// Use for file-private implementation details
Inline Namespaces (C++11)
// For library versioning
namespace MyLibrary {
inline namespace v2 { // Current default version
void function() { cout << "v2" << endl; }
}
namespace v1 {
void function() { cout << "v1" << endl; }
}
}
MyLibrary::function(); // Uses v2 (inline namespace)
MyLibrary::v1::function(); // Explicitly uses v1
MyLibrary::v2::function(); // Explicitly uses v2
Best Practices
✅ Do
// 1. Use #pragma once for include guards
#pragma once
// 2. Use constexpr/const instead of #define for constants
constexpr double PI = 3.14159;
const int MAX_SIZE = 100;
// 3. Use inline functions instead of function-like macros
inline int square(int x) { return x * x; }
// 4. Use namespaces to organize code
namespace MyProject {
// Your code here
}
// 5. Use namespace aliases for long names
namespace fs = std::filesystem;
// 6. Limit using declarations to .cpp files
using std::cout;
using std::endl;
❌ Don't
// 1. Don't use using namespace in headers
// header.h
using namespace std; // ❌ BAD!
// 2. Don't use macros when alternatives exist
#define MAX_VALUE 100 // Use constexpr instead
// 3. Don't create overly complex macros
#define COMPLEX_MACRO(a, b, c, d) /* hard to debug */
// 4. Don't forget include guards
// Missing #pragma once leads to redefinition errors
// 5. Don't use reserved names (double underscore)
#define __MY_MACRO // ❌ Reserved for implementation
#define _MyMacro // ❌ Reserved when followed by uppercase
Quick Reference
// Include directives
#include <system_header>
#include "user_header.h"
#pragma once
// Macros
#define NAME value
#define FUNC(x) ((x) * 2)
#undef NAME
// Conditional compilation
#if condition
#elif condition
#else
#endif
#ifdef NAME
#ifndef NAME
#if defined(NAME)
// Predefined macros
__FILE__ __LINE__ __DATE__ __TIME__ __func__ __cplusplus
// Namespaces
namespace Name { }
namespace A::B::C { } // C++17
using namespace Name;
using Name::symbol;
namespace Alias = Long::Namespace::Name;
Compile & Run
g++ -std=c++17 -Wall -Wextra examples.cpp -o examples
./examples
# See preprocessor output
g++ -E source.cpp > preprocessed.cpp
# Define macro from command line
g++ -DDEBUG -DMAX_SIZE=200 source.cpp -o output