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
#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
#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
#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
#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:
#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++03201103L
for C++11201402L
for C++14201703L
for C++17202002L
for C++20
Platform and Architecture Detection
Predefined macros are especially useful for writing cross-platform code:
#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:
#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
#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
#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
#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:
Macro | Description |
---|---|
__FILE__ | Current source filename |
__LINE__ | Current line number |
__func__ | Current function name |
__DATE__ | Compilation date |
__TIME__ | Compilation time |
__cplusplus | C++ standard version |
__STDC__ | Standard C compatibility |
_WIN32 | Defined on Windows (32 or 64 bit) |
_WIN64 | Defined 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_ARCH | Defined for ARM architecture |
__GNUC__ | GCC compiler version |
_MSC_VER | Visual 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
-
Create a simple program that displays different welcome messages based on the operating system it's running on.
-
Write a macro that logs function entries with the function name, file, and line number.
-
Create a compatibility layer that uses different code paths for C++11, C++14, and C++17 standards.
-
Write a program that checks if a specific compiler feature is available and provides an alternative implementation if it's not.
-
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! :)