Skip to main content

C++ Predefined Macros

Introduction

Predefined macros are special identifiers that the C++ compiler automatically defines for you. Unlike regular macros that you define with #define, predefined macros are built into the compiler itself. They provide valuable information about the compilation environment, platform details, and can help you write more portable code.

In this tutorial, we'll explore the most commonly used predefined macros in C++, understand their purpose, and see practical examples of how to use them in your programs.

What Are Predefined Macros?

Predefined macros (also called built-in macros) are identifiers that begin with double underscores (__) or a single underscore followed by a capital letter. They are automatically defined by the compiler and provide information about:

  • The current source file
  • Compilation date and time
  • Compiler version and features
  • Platform and architecture details
  • Language standard compliance

Let's explore the most useful predefined macros that C++ provides.

Standard Predefined Macros

C++ guarantees that the following macros are available in all standard-compliant implementations:

File and Line Information

cpp
#include <iostream>

int main() {
std::cout << "Current file: " << __FILE__ << std::endl;
std::cout << "Current line: " << __LINE__ << std::endl;

// Let's see how __LINE__ changes
std::cout << "This is line " << __LINE__ << std::endl;
std::cout << "And this is line " << __LINE__ << std::endl;

return 0;
}

Output:

Current file: /path/to/your/file.cpp
Current line: 4
This is line 7
And this is line 8
  • __FILE__: Expands to the current source filename (as a string literal)
  • __LINE__: Expands to the current line number (as a decimal integer)

Date and Time Information

cpp
#include <iostream>

int main() {
std::cout << "Compilation date: " << __DATE__ << std::endl;
std::cout << "Compilation time: " << __TIME__ << std::endl;
return 0;
}

Output:

Compilation date: Mar 15 2023
Compilation time: 14:32:17
  • __DATE__: Expands to the date of compilation in "Mmm DD YYYY" format
  • __TIME__: Expands to the time of compilation in "HH:MM:SS" format

Function Information

cpp
#include <iostream>

void printFunctionName() {
std::cout << "Current function: " << __func__ << std::endl;
}

int main() {
std::cout << "Current function: " << __func__ << std::endl;
printFunctionName();
return 0;
}

Output:

Current function: main
Current function: printFunctionName
  • __func__: Expands to the name of the current function (as a string literal)

Compiler-Specific Predefined Macros

Different compilers define their own set of predefined macros to provide information about the compiler itself.

Compiler Identification

cpp
#include <iostream>

int main() {
#ifdef __GNUC__
std::cout << "Compiled with GCC version: "
<< __GNUC__ << "." << __GNUC_MINOR__ << "." << __GNUC_PATCHLEVEL__
<< std::endl;
#endif

#ifdef _MSC_VER
std::cout << "Compiled with Microsoft Visual C++ version: "
<< _MSC_VER
<< std::endl;
#endif

#ifdef __clang__
std::cout << "Compiled with Clang version: "
<< __clang_major__ << "." << __clang_minor__ << "." << __clang_patchlevel__
<< std::endl;
#endif

return 0;
}

Output (when compiled with GCC 11.2.0):

Compiled with GCC version: 11.2.0

C++ Standard Version Macros

You can detect which C++ standard is being used during compilation:

cpp
#include <iostream>

int main() {
#ifdef __cplusplus
std::cout << "C++ standard version: " << __cplusplus << std::endl;

#if __cplusplus >= 201703L
std::cout << "Using C++17 or newer" << std::endl;
#elif __cplusplus >= 201402L
std::cout << "Using C++14" << std::endl;
#elif __cplusplus >= 201103L
std::cout << "Using C++11" << std::endl;
#else
std::cout << "Using C++98/03" << std::endl;
#endif
#else
std::cout << "Not being compiled as C++" << std::endl;
#endif

return 0;
}

Output (when compiled with C++17):

C++ standard version: 201703
Using C++17 or newer

The __cplusplus macro contains the version of the C++ standard being used:

  • 199711L for C++98/C++03
  • 201103L for C++11
  • 201402L for C++14
  • 201703L for C++17
  • 202002L for C++20

Platform and Architecture Detection

Predefined macros are especially useful for writing cross-platform code:

cpp
#include <iostream>

int main() {
// Operating System Detection
#if defined(_WIN32)
std::cout << "Running on Windows" << std::endl;
#if defined(_WIN64)
std::cout << "64-bit Windows" << std::endl;
#else
std::cout << "32-bit Windows" << std::endl;
#endif
#elif defined(__APPLE__)
std::cout << "Running on Apple platform" << std::endl;
#include <TargetConditionals.h>
#if TARGET_OS_IPHONE
std::cout << "iOS device" << std::endl;
#elif TARGET_OS_MAC
std::cout << "macOS" << std::endl;
#endif
#elif defined(__linux__)
std::cout << "Running on Linux" << std::endl;
#elif defined(__unix__)
std::cout << "Running on Unix" << std::endl;
#else
std::cout << "Unknown operating system" << std::endl;
#endif

// Architecture Detection
#if defined(__x86_64__) || defined(_M_X64)
std::cout << "x86-64 architecture" << std::endl;
#elif defined(__i386) || defined(_M_IX86)
std::cout << "x86 architecture" << std::endl;
#elif defined(__ARM_ARCH) || defined(_M_ARM)
std::cout << "ARM architecture" << std::endl;
#else
std::cout << "Unknown architecture" << std::endl;
#endif

return 0;
}

Output (on a 64-bit Windows machine):

Running on Windows
64-bit Windows
x86-64 architecture

Practical Applications

1. Debug Information

One of the most common uses of predefined macros is to create helpful debug output:

cpp
#include <iostream>

// Custom debug macro that shows file, line, and message
#define DEBUG_INFO(msg) std::cout << "[DEBUG] " << __FILE__ << ":" << __LINE__ << " - " << msg << std::endl

int main() {
int x = 10;
DEBUG_INFO("Variable x has value: " << x);

x += 5;
DEBUG_INFO("Updated x to: " << x);

return 0;
}

Output:

[DEBUG] /path/to/your/file.cpp:8 - Variable x has value: 10
[DEBUG] /path/to/your/file.cpp:11 - Updated x to: 15

2. Conditional Compilation for Different Platforms

cpp
#include <iostream>
#include <string>

std::string getDataDirectory() {
#if defined(_WIN32)
return "C:\\ProgramData\\MyApp\\";
#elif defined(__APPLE__)
return "/Library/Application Support/MyApp/";
#elif defined(__linux__)
return "/var/lib/myapp/";
#else
#error "Unsupported platform"
#endif
}

int main() {
std::cout << "Data directory: " << getDataDirectory() << std::endl;
return 0;
}

3. Creating a Version Number

cpp
#include <iostream>
#include <string>

// Define application version using predefined macros
#define APP_VERSION_STR "1.2.3"
#define BUILD_DATE_STR __DATE__ " " __TIME__

std::string getVersionInfo() {
std::string info = "MyApp v" + std::string(APP_VERSION_STR) + "\n";
info += "Build: " + std::string(BUILD_DATE_STR) + "\n";

#ifdef __cplusplus
info += "C++ Standard: " + std::to_string(__cplusplus) + "\n";
#endif

#ifdef __GNUC__
info += "Compiled with GCC " +
std::to_string(__GNUC__) + "." +
std::to_string(__GNUC_MINOR__) + "\n";
#endif

return info;
}

int main() {
std::cout << getVersionInfo() << std::endl;
return 0;
}

Output:

MyApp v1.2.3
Build: Mar 15 2023 14:32:17
C++ Standard: 201703
Compiled with GCC 11.2

4. Backward Compatibility

cpp
#include <iostream>
#include <vector>

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};

#if __cplusplus >= 201703L
// Using C++17 features
if (std::size(numbers) > 3) {
std::cout << "Vector has more than 3 elements" << std::endl;
}
#else
// Fallback for older C++ standards
if (numbers.size() > 3) {
std::cout << "Vector has more than 3 elements" << std::endl;
}
#endif

return 0;
}

Common Predefined Macros Table

Here's a summary table of frequently used predefined macros:

MacroDescription
__FILE__Current source filename
__LINE__Current line number
__func__Current function name
__DATE__Compilation date
__TIME__Compilation time
__cplusplusC++ standard version
__STDC__Standard C compatibility
_WIN32Defined on Windows (32 or 64 bit)
_WIN64Defined on 64-bit Windows
__APPLE__Defined on macOS/iOS platforms
__linux__Defined on Linux platforms
__unix__Defined on Unix platforms
__x86_64__Defined for x86-64 architecture
__i386__Defined for x86 architecture
__ARM_ARCHDefined for ARM architecture
__GNUC__GCC compiler version
_MSC_VERVisual C++ compiler version
__clang__Clang compiler version

Summary

Predefined macros in C++ offer a powerful way to access information about the compilation environment, detect platform and compiler features, and write more portable code. They are automatically defined by the compiler and can help you customize your code's behavior based on different conditions.

Key points to remember:

  • Predefined macros start with __ or a single _ followed by a capital letter
  • They provide information about files, lines, compilation time, compilers, and platforms
  • They are essential for writing cross-platform and portable code
  • They help in debugging and creating informative error messages
  • Different compilers may define different sets of predefined macros

Exercises

  1. Create a simple program that displays different welcome messages based on the operating system it's running on.

  2. Write a macro that logs function entries with the function name, file, and line number.

  3. Create a compatibility layer that uses different code paths for C++11, C++14, and C++17 standards.

  4. Write a program that checks if a specific compiler feature is available and provides an alternative implementation if it's not.

  5. Create a debugging utility that only prints debug information when compiled in debug mode (hint: use compiler flags and predefined macros to detect debug mode).

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)