Why use preprocessor #if statements instead of if() else? - c++

I see this being done all the time for example in the Linux Kernel. What is the purpose of using the preprocessor commands vs just normal C++ if else block? Is there a speed advantage or something?

A preprocessor changes the C/C++ code before it gets compiled (hence pre processor).
Preprocessor ifs are evaluated at compile-time.
C/C++ ifs are evaluated at run-time.
You can do things that can't be done at run-time.
Adjust code for different platforms or different compilers:
#ifdef __unix__ /* __unix__ is usually defined by compilers targeting Unix systems */
#include <unistd.h>
#elif defined _WIN32 /* _Win32 is usually defined by compilers targeting 32 or 64 bit Windows systems */
#include <windows.h>
#endif
Ensure header file definitions are included only once (equivalent of #pragma once, but more portable):
#ifndef EXAMPLE_H
#define EXAMPLE_H
class Example { ... };
#endif
You can make things faster than at run-time.
void some_debug_function() {
#ifdef DEBUG
printf("Debug!\n");
#endif
}
Now, when compiling with DEBUG not defined (likely a command line parameter to your compiler), any calls to some_debug_function can be optimized away by the compiler.

Preprocessor is run before the compilation pass, so the compiler won't even see anything that was in the not-taken #if branch.
#if DEBUG
int a;
#else
double b;
#endif
gcc -c -DDEBUG=1 file.c will see "int a"
gcc -c file.c will see "double b"

Preprocessor allows you to actually cut out or paste in to your source file, code to be compiled. If its cut out, its gone, its like a comment, does nothing, is not compiled, produces no code in the binary. Devs will often use this technique to add code only in debug build for debugging purposes or for adding or excluding code for specific operating systems.

Related

Is there a way to detect if a given header of the C++ Standard Library is included?

More specifically, is it possible to detect, at compilation time, if a C++ standard header file is included (let say <complex>), and do that in a way that is cross-plateform and cross-compiler ? I'd like it to work with C++11 at least, but for the question's sake, is it possible for any C++ standard ?
I know the implementation of the C++ Standard Library is dependent on the compiler and operating system, but is there a standardized guard name for the header, a macro that I can check the existence of ?
I've run into this : Detect usage of STL in C++ , which is a hack not to use the headers, and this : Is there a way to detect portably that a standard header is included using macros?, which answers with a C++17 feature, and is in fact more about detecting a function.
What I want is to be able to write some preprocessor instruction at a given point in the code that will tell whether the header is included (yet). I checked the source code of (an implementation of) <complex> as an example, and _GLIBCXX_COMPLEX is defined, but probably only for this implementation.
TL;DR : No, there is not, apart from the C++17 feature #if __has_include(<complex>). Not with defined macro anyway.
Long answer : there are two parts to the answer. What I read from some headers, and the tests I ran on differents OS with differents compilers.
Warning : I will write also about C, as some compilers uses the same macros. I also use the shortcut 'for C' and 'for C++' instead of 'when the header complex.h is included' and 'when the header <complex> is included'.
Ok, here's my best answer as for now.
About C and complex.h :
In C, the existence of the macro
_Complex_I is the only macro that I think should be defined in any complex.h header, as it is specified by the ISO C99 (cf. ISO/IEC 9899:1999/TC3 text). The tests I ran shows that this is a realistic hypothesis. There are some additionnal macro used, such as _COMPLEX_H that seems to be defined when compiling with GCC,
and _C_COMPLEX_T when compiling with MSVC. There might be others, I didn't find them, please don't hesitate to add them.
But it is C, not C++, and one should not mix both.
About C++ and <complex> :
The C++ standard (ISO/CEI 14882:2011) does not specify such macro. In fact, it does not specify the inclusion of any macro (at first glance).
It does specify the existence of the complex class however, but this does not answer the question.
For what I read, _COMPLEX_ and _C_COMPLEX_T seems to be defined in the <complex> header for
LLVM-clang and MSVC, but also in the complex.h for the apple implementation of the C math library (libm).
However, my tests with the LLVM-clang compiler on macOS failed showing those macro, and I can't find the file where I read that information.
I also found that g++ is defining _GLIBCXX_COMPLEX.
If one wanted to do that with a C++ older than C++17, a solution would be to test with the given compilers and architectures the code will run onto.
That is quite bad, but it's the closest answer I get.
The tests :
I give here the code that I run on the different OS, the compiler options, their version, and the results they prompt. _Complex_I was indeed defined for all C headers.
I wasn't able to run the test on macOS with GCC and G++, but I bet that _Complex_I will be defined with GCC and _GLIBCXX_COMPLEX with G++.
Tests for C++
G++ -std=c++11 v9.3.0 on Ubuntu 20.04 :
_GLIBCXX_COMPLEX
G++ -std=c++11 v11.2.0 on windows 10 (via Cygwin) :
_GLIBCXX_COMPLEX
MSVC default c++11 option v14.29.xxxxx.x on windows 10 :
_C_COMPLEX_T
_COMPLEX_
Apple LLVM-clang -std=c++11 v10.0.1 : None (doesn't mean there is none, just that I don't know which one)
Code for C++:
#include <iostream>
#include <complex>
int main(){
#ifdef _COMPLEX_
std::cout << "_COMPLEX_" << std::endl;
#endif
#ifdef _C_COMPLEX_T
std::cout << "_C_COMPLEX_T" << std::endl;
#endif
#ifdef _COMPLEX_H
std::cout << "_COMPLEX_H" << std::endl;
#endif
#ifdef _Complex_I
std::cout << "_Complex_I" << std::endl;
#endif
#ifdef _GLIBCXX_COMPLEX
std::cout << "_GLIBCXX_COMPLEX" << std::endl;
#endif
return 0;
}
Tests for C :
GCC -std=c99 v9.3.0 on Ubuntu 20.04 :
_Complex_I _COMPLEX_H
GCC -std=c99 v11.2.0 on windows 10 (via Cygwin) :
_Complex_I _COMPLEX_H
MSVC -std=c99 v14.29.xxxxx.x on windows 10 :
_Complex_I _C_COMPLEX_T
Apple LLVM-clang -std=c99 v10.0.1 : _Complex_I
Code for C:
#include <stdlib.h>
#include <stdio.h>
#include <complex.h>
int main(){
#ifdef _COMPLEX_
printf("_COMPLEX_\n");
#endif
#ifdef _C_COMPLEX_T
printf("_C_COMPLEX_T\n");
#endif
#ifdef _COMPLEX_H
printf("_COMPLEX_H\n");
#endif
#ifdef _Complex_I
printf("_Complex_I\n");
#endif
#ifdef _GLIBCXX_COMPLEX
printf("_GLIBCXX_COMPLEX\n");
#endif
return 0;
}
To sum up :
MSVC is defining _C_COMPLEX_T whatever C or C++ used, and also _COMPLEX_ for C++. _Complex_I is always defined for C, and G++ seems to define _GLIBCXX_COMPLEX whatever the plateform (test to be done on macOS) for C++.

#ifdef _WIN32 not getting detected

I can not get the #ifdef rule to work at least on windows (64 bit).
Compiler version is g++ 5.4.0
I have tried:
#ifdef _WIN32
#ifdef _WIN64
#ifdef OS_WINDOWS
I have compiled the following test with:
g++ main.cpp
Even with a simple code as this:
#include <iostream>
int main()
{
std::cout << "you are on...";
#ifdef _WIN32
std::cout << "Windows" << std::endl;
#elif __linux__
std::cout << "Linux" << std::endl;
#endif
return 0;
}
Output is:
"you are on..."
...and nothing else gets couted out.
#ifdef _WIN32
#ifdef _WIN64
These are pre-defined macros defined by the MSVC compiler. You appear to be using g++ instead. That probably means either MinGW, or Cygwin.
Here and here are collections of macros pre-defined by several compilers.
MinGW __MINGW32__
Cygwin __CYGWIN__
If you prefer to not to build hefty ifdef - else trees, and scour the internet for macros defined by obscure compilers, and their different versions, I recommend to instead include a few headers from boost. They have already done the hard part of the work. Although, note that BOOST_OS_WINDOWS is separate from BOOST_OS_CYGWIN.
Use __CYGWIN32__ to detect Windows when compiling g++ in cygwin. (This is defined in both 32 and 64 bit).
_WIN32 &c. may not be defined in that case. It isn't for me.
(As also mentioned in a comment; using echo | g++ -dM -E to output the list of what is defined can be helpful.)
yes, it is true that this:
#ifdef _WIN32
sometimes doesn't work on Windows (!) In this case, use the following form instead:
#if defined(_WIN32)
I was surprised but I have seen that problem and it helped me!

problems in migrating 32bit application on 64 bit

I am trying to migrate existing c++ 32 code to 64 code on windows7 with visual studio 2010.i never did 64bit compilation before. with the help of internet references i did the setup for 64 bit compilation. like VS2010 with 64 bit compiler etc and other configuration changes.
In the preprocessor i removed WIN32 and added WIN64. i have some other pre-processors like OS_WIN_32 and some other which are specific in my code.
In the code wherever WIN32 was getting used i added extra condition as || WIN64 this is just to ensure that application should get compiled with win32 as well as win64.
When i am trying to compile the code i am getting the compilation error saying
fatal error C1189: #error : Only one of the WIN32 and WIN64 symbols should be defined
this error is coming from the local code where we have a check whether both WIN32 and WIN64 are defined. that code is as shown below.
#if defined WIN32 && defined WIN64
# error Only one of the WIN32 and WIN64 symbols should be defined
#endif
in VS2010 if macros are not enabled then the code inside the macro gets greyed out. in my code also the above error is greyed out. but still i am getting that error.
The code where i added WIN64 is including windows.h. for reference givine it below.
#if defined WIN32 || defined WIN64
#include <windows.h>
#include <process.h>
#endif
So my question is why i am getting this error? shouldnt we add windows.h for 64bit compilation.? i tried by commenting this inclusion but i am getting other errors wrt HANDLE which are used in the code.
If i go to WIN32 definition VS2010 is pointing to a definition in windef.h file. This file is present in Microsoft SDKs\windows\v7.0A\include folder i.e. not my local code.
for referance that definition is given below.
#ifndef WIN32
#define WIN32
#endif
So i want to know why compiler is getting both pre-processors WIN32 and WIN64.
Thanks in advance for your help.
You shouldn't define either yourself. The macro's that should be used to check this are
_WIN32 // always defined for Windows apps
_WIN64 // only defined for x64 compilation
These are defined by the compiler (see here).
Often, the IDE adds the unprefixed macros to the commandline to not let legacy projects which use the non-documented unprefixed versions fail to build. The fact that they work is not a reason to use them, when documented alternatives are present.
It boils down to this:
#ifdef _WIN32
// We're on Windows, yay!
#ifdef _WIN64
// We're on x64! Yay!
#else // _WIN64
// We're on x86 (or perhaps IA64, but that one doesn't matter anymore). Yay!
#endif // _WIN64
#else // _WIN32
// We're not on Windows, maybe WindowsCE or WindowsPhone stuff, otherwise some other platform
#endif

how to undefine _MSC_VER?

I work in Visual Studio but my project is for a POSIX-based environment (marmalade sdk). In this project, the release
build is compiled with gcc for ARM but the debug version works on windows and is compiled by MS compiler. Also this environmet has its own implementation of STL and other standard libraries.
Many of these c++ librares have code like this:
#if defined( _MSC_VER )
#include <Windows.h>
#else
#include <pthread.h>
#endif
Is it possible to undefine the _MSC_VER macro? - So that the C++ libraries will detect a POSIX system here.
_MSC_VER is (and always should be) defined when compiling with the Microsoft compiler so that it "evaluates to the major and minor number components of the compiler's version number". Therefore, the code is using the wrong macro test, since it will always be defined to some value for your compiler regardless of the Windows environment differences.
Rather than destroy the definition of _MSC_VER (which could lead to other problems if any code really does want to know the compiler version), what you really should do instead is to correct the condition so that a more appropriate macro test is used that distinguishes between the kinds of Windows environments that you might encounter.
See the more complete list of predefined macros you could consider here
You could either replace the condition ...
#if someOtherConditionGoesHere
... or extend it with additional conditions, e.g.
#if defined(_MSC_VER) && someOtherConditionGoesHere
Of course:
#undef _MSC_VER
#if defined( _MSC_VER )
#include <Windows.h>
#else
#include <pthread.h>
#endif
Or, #undef it before you include the file where _MSC_VER is used.

C++ preprocessor unexpected compilation errors

Please look at the following file: (it is a complete file)
#ifndef TEES_ALGORITHM_LIBRARY_WRAPPER_H
#define TEES_ALGORITHM_LIBRARY_WRAPPER_H
#ifdef _TEES_COMPILE_AS_LIB
#include <dfa\Includes\DFC_algorithms.hpp>
#include <DFA\FuzzyClassifier\FuzzyAlgorithmIntialization\InitFuzzyAlgorithm.hpp>
typedef teesalgorithm::tees_fuzzy_algorithms algorithms_switchyard_class;
#else
#include <DFA\Includes\commercial_algorithms.hpp>
//An incomplete class to hide implementation
class algorithms_switchyard_class;
#endif
class AlgorithmLibraryWrapper {
algorithms_switchyard_class * algorithmPtr_;
typedef teesalgorithm::tees_paramObj paramObj_type;
typedef teesalgorithm::tees_errorObj errorObj_type;
typedef teesalgorithm::tees_statusObj statusObj_type;
typedef teesalgorithm::tees_dataObj dataObj_type;
typedef teesalgorithm::tees_outputObj outputObj_type;
public:
AlgorithmLibraryWrapper(const std::string& sAlgName, paramObj_type& paramObj, errorObj_type& errObj, statusObj_type& statusObj, const char* sFilePath);
static bool dataReader(const std::string& sFileName, dataObj_type& dataObj, errorObj_type& errObj, statusObj_type& statusObj);
bool runalgorithm(const dataObj_type& dataObj, outputObj_type& outObj, errorObj_type& errObj, statusObj_type& statusObj);
~AlgorithmLibraryWrapper();
};
#ifdef _TEES_USE_COMPILED_ALGORITHM_LIB
# ifdef _MSC_VER
#if _MSC_VER < 1400 // If VC 2003
#ifdef _DEBUG
#error No AlgorithmLibWrapper libraries compiled for this version of VC
#else
#error No AlgorithmLibWrapper libraries compiled for this version of VC
#endif
#elif defined(UNDER_CE) // Win CE
#ifdef _DEBUG
#pragma comment( lib, "AlgorithmLibWrapperCEd" )
#else
#pragma comment( lib, "AlgorithmLibWrapperCE" )
#endif
#else // If VC 2005
#ifdef _DEBUG
#pragma comment( lib, "AlgorithmLibWrapperd" )
#else
#pragma comment( lib, "AlgorithmLibWrapper" )
#endif
#endif
#endif
#endif
#endif //TEES_ALGORITHM_LIBRARY_WRAPPER_H
I am getting the following errors; I don't know why. I manually counted the preprocessor directives also.
AlgorithmLibraryWrapper.hpp:10:1: unterminated #ifdef
AlgorithmLibraryWrapper.hpp:7:1: unterminated #ifndef
I am using the poor vxWorks gcc compiler. Please let me know if the fault is mine or the compiler's.
It could be that the problem is in the included files (if there actually are unbalaced #if/#endifs.
I would try preprocessing with another compiler. You can use gcc for that, doesn't matter it wouldn't compile. Just get gcc (or MinGW if you're on Windows) and run
cpp -Iinclude_direcories your_file
Or, if you don't like gcc, get MSVC Express edition. Again, you can preprocess code that even doesn't compile, so no problem with nonworking library etc.
Most compilers have an option that will give you the output from the preprocessor so you can check what it's doing. For example,
gcc -E file.c >file.preproc
will give you the pre-processed source so you can check the balancing of #if against #endif.
At a guess, one of the files you are #including from this one has a mismatched #ifdef/#endif pair. You need to look at all the files (as the preprocesor does), not just this one.
As others have noted, this is most likely due to mismatched include guards.
If the files you are including are under your control (i.e. not part of a 3rd party closed source library), you could consider replacing the #ifndef et. al. guards (which are used to prevent multiple inclusion) with #pragma once. This will eliminate the possibility of having mismatched preprocessor directives.
One caveat of this is that pragma once is non-standard, so it will only work if your compiler supports it.
I have tried to compile your source using vs 6.0 but did not get the error you have mentioned. As others said may be the error is coming from the included header file . For me to get your code compiled i need to comment the above header.
Please check the above header once.
I'd debug this by commenting out sections one by one and trying to identify which section is causing the error.
It could be your compiler does not like the nested #ifdefs or does not interpret the syntax correctly. Maybe it doesn't like the #pragmas.
This is a long shot, but in your source file you have the following line:
# ifdef _MSC_VER
where there is whitespace between the '#' character and the directive name (ifdef). This is valid in C/C++; however, it's not too commonly seen so I wouldn't be very surprised if the odd compiler choked on it.