Show Lecture.ConditionalCompilation as a slide show.
CS253 Conditional Compilation
Buridan's Ass
Review
You can make decisions at compile-time (as opposed to run-time)
via #if
and friends:
cout << "alpha\n";
#if 2 > 3
cout << "beta\n";
#elif 4 != 5
cout << "gamma\n";
#endif
alpha
gamma
Review
You can also “comment out” sections of code:
cout << "red\n";
#if 0
this doesn’t work yet
cout << "white\n";
#endif
cout << "blue\n";
red
blue
Conditional Debugging
You can use a flag to control conditional compilation:
const auto now = time(nullptr);
#if DEBUG
cout << "now is " << now << '\n';
#endif
string filename = "/tmp/temp-" + to_string(now);
cout << "filename: " << filename << '\n';
filename: /tmp/temp-1732267895
The flag DEBUG
isn’t defined, so the process id isn’t printed.
Conditional Debugging
Let’s enable flag using #define:
#define DEBUG 1
const auto now = time(nullptr);
#if DEBUG
cout << "now is " << now << '\n';
#endif
string filename = "/tmp/temp-" + to_string(now);
cout << "filename: " << filename << '\n';
now is 1732267895
filename: /tmp/temp-1732267895
You can also turn the flag on via: g++ -DDEBUG
Compiler Version
Most compilers provide ways to check the compiler version
at compile-time. The gcc/g++ compiler provides
several symbols:
cout << "Major " << __GNUC__ << '\n'
<< "Minor " << __GNUC_MINOR__ << '\n'
<< "Patch " << __GNUC_PATCHLEVEL__ << '\n';
Major 8
Minor 3
Patch 1
cout << __GNUC__ << '.'
<< __GNUC_MINOR__ << '.'
<< __GNUC_PATCHLEVEL__ << '\n';
8.3.1
Compiler Version
You can use the symbols to decide whether a feature is available:
#if __GNUC__ > 25
string filename = generate_temporary_filename();
#else
string filename = "/tmp/temp-" + to_string(rand())
+ '-' + to_string(time(nullptr));
#endif
cout << "filename: " << filename << '\n';
filename: /tmp/temp-1804289383-1732267895
C++ Standard Version
Use __cplusplus
to determine C++ version.
Its value is the YYYYMM of standardization.
vector<long double> v;
for (int i=1; i<=10000; i++)
v.push_back(i*i);
#if __cplusplus >= 201103L
const auto old = v.capacity();
v.shrink_to_fit();
cout << old << " ➔ " << v.capacity();
#endif
16384 ➔ 10000
.shrink_to_fit()
didn’t exist in C++ before 2011.
- Why the
L
in 201100L
?
System detection
Every compiler defines symbols that allow you to
detect the compiler or architecture:
__WIN32__
__APPLE__
__linux__
__GNUC__
__hpux
See https://sourceforge.net/p/predef/wiki/OperatingSystems/
System-Dependent Code
#if __linux__
cout << "On Linux\n";
#endif
#if __APPLE__
cout << "On an Apple product\n";
#endif
#if __hpux
cout << "On HP-UX\n";
#endif
On Linux
- This code behaves differently on different systems.
- Cute, but is it useful? Yes.
Example
- Imagine that you want to write a function that returns
the current program name, for error messages.
- “No problem”, you say, “I can get that from
argv
[0]
.”
- Yes, but that requires the user to pass
argv
to this function.
- How inconvenient!
- What if we’re nine levels deep in function calls?
- What if we’re writing a library, where we have no access to
argv
?
Partial Solution
Most compilers have system-dependent ways of solving this problem.
The real problem is knowing which one to use:
Linux solution:
cout << "I am " << program_invocation_name;
I am ./a.out
HP-UX solution:
extern const char **__argv_value;
cout << "I am " << __argv_value[0];
/tmp/ccDcamGL.o: In function `main':
c.cc:(.text+0x19): undefined reference to `__argv_value'
collect2: error: ld returned 1 exit status
Real solution
Of course, the real solution uses conditional compilation!
#if __linux__
string pname() { return program_invocation_name; }
#elif __hpux
extern const char **__argv_value;
string pname() { return __argv_value[0]; }
#else
#error I can’t deal with this system!
#endif
int main() {
cout << pname() << '\n';
}
./a.out
Hide the OS-specific code in the definition of pname()
.
The rest of the program is clean, beautiful, and DRY.
False solution
The test must occur at compile time—can’t define a function twice!
if (__linux__) {
string pname() { return program_invocation_name; }
}
else if (__hpux) {
extern const char **__argv_value;
string pname() { return __argv_value[0]; }
}
else {
// I can’t deal with this system!
}
int main() {
cout << pname() << '\n';
}
c.cc:1: error: expected unqualified-id before 'if'