What is the right way of conditionally declaring a macro in C++? - c++

I'm working with the following project structure
Project
|
| -Lib
| -lib.vcxproj
| -lib.h
| -lib.cpp
| -UnitTest
| -unittest.vcxproj
| -driver.cpp
| -constants.h
Now my constants.h looks like below
#ifndef UNITTESTS_MACROS_H
#define UNITTESTS_MACROS_H
#ifdef UNITTESTS
#define MY_TEST_MACRO 1
#else
#define MY_TEST_MACRO 0
#endif
#endif // UNITTESTS_MACROS_H
I have defined the UnitTests constant in the unittest.vcxproj file. Using following code
<PreprocessorDefinitions>
%(PreprocessorDefinitions);
UNITTESTS
</PreprocessorDefinitions>
I'm including constants.h in driver.cpp and lib.h. I want the macro MY_TEST_MACRO to be initialized to 1 when I'm compiling the UnitTest. But despite giving the preprocessor macro in unittest.vcxproj, the macro MY_TEST_MACRO is not initialized to 1 while accessing through lib.h. I'm including reference to constants.h in lib.vcxproj through the following
<AdditionalIncludeDirectories>tag.
How do I initialize MY_TEST_MACRO to 1 while building UnitTest and keep it 0 while building Lib on its own?

Those are separate projects, so value should be changed for both of them at same time. Rather you should do that via configurations, by utilizing output of test-enabled configuration of Lib in unittest project.

Related

Setting/understanding how #define is used in a header file (setting from cmakelist)

I am writing a simple logging library and I am trying to set a compile time definition on the logging level. If I put the #define LOG_COMPILE_MIN_LEVEL in the header file it is not being set at compile time, however if the #define LOG_COMPILE_MIN_LEVEL is in the c file it does work.
Here are snippets of the relevant sections
CMakeLists.txt
// Trying to set LOG_COMPILE_MIN_LEVEL to ERROR
add_definitions(-DLOG_COMPILE_MIN_LEVEL=ERROR)
# I have also tried
add_compile_definitions(LOG_COMPILE_MIN_LEVEL=ERROR)
In log.h
// I define in this file the following (technically they are enums with values set)
// DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3
// By default resort to INFO if not found
#ifndef LOG_COMPILE_MIN_LEVEL
#define LOG_COMPILE_MIN_LEVEL INFO
#endif
// In log.h I am trying to do this, where before even going to
// the log function (log_log) the level is checked against the compile level
// So since I (in theory) set LOG_COMPILE_MIN_LEVEL to ERROR, any INFO levels
// are not logged since INFO (value of 1) is below LOG_COMPILE_MIN_LEVEL which is
// set to ERROR (value of 3)
// Ideally/in theory the compiler will compile out any messages not used by doing this....
#define LOG(level, __FILENAME__, __LINE__, ...) \
if (level < LOG_COMPILE_MIN_LEVEL);\
else log_log(level, __FILENAME__, __LINE__, __VA_ARGS__)
// The reason for having LOG and LOG_COMPILE_MIN_LEVEL in the header is cause I provide
// some logging macros like:
#define LOG_ERROR(...) LOG(ERROR, __FILENAME__, __LINE__, __VA_ARGS__)
// In some c/cpp file, this should not work since log level is set to
// ERROR but it does (because the default value of LOG_COMPILE_MIN_LEVEL is
// info.
LOG_INFO("My info message);
No matter what I have tried in the CMakeLists.txt I cannot get it to work. Now when I move
#ifndef LOG_COMPILE_MIN_LEVEL
#define LOG_COMPILE_MIN_LEVEL INFO
#endif
To a c/cpp file it does work (gets set to ERROR).
Why?
What is happening?
How can I set it for the header file?
The reason I am trying to set/define it in the header file is for #define LOG method.
What could be wrong
Although the example is not proper for reproduction, and the definition of the INFO is not provided, based on the explanation that the token is defined as enum, I have strong filling that this is the problem. The enum types do not mix well with the macros, since enum are constructed in the compile time and macros are evaluated in the pre-processing time (before the compile starts).
Working example
The approach should work from both .c and .h files. I have made a minimal example and it worked fine:
test.h
#define DEBUG = 0
#define INFO = 1
#define WARN = 2
#define ERROR = 3
#ifndef LOG_COMPILE_MIN_LEVEL
#define LOG_COMPILE_MIN_LEVEL INFO
#endif
test.c
#include "test.h"
int main(int argc, char *argv) {
printf("%i\n",LOG_COMPILE_MIN_LEVEL);
#if LOG_COMPILE_MIN_LEVEL == 1
printf("Log set to INFO\n");`
#endif
}
After passing the file trough the pre-processor stage only gcc -E test.c, I get the following output:
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "test.c"
# 1 "test.h" 1
# 2 "test.c" 2
int main(int argc, char *argv) {
printf("%i\n",1);
printf("Log set to INFO\n");`
}
as you can see, the pre-processor had recognized the macros and had performed the replacement properly.
Troubleshooting
The C/C++ compilers are allowing you to produce the pre-processed file only. The role of the pre-processed file is to help in the debugging of problems like this, so create a minimal example (like my case) and pass it only trough the pre-processor. For GCC, the -E is the instruction telling the complier to produce and present on stdout the pre-processed version of the file.

Expand defines with GCC

How can I expand or collapse the defines in the header file for example
#define IOC_IN 0x80000000
#define IOC_WS2 0x08000000
#define _WSAIOW(x,y)(IOC_IN|(x)|(y))
#define SIO_ASSOCIATE_HANDLE _WSAIOW(IOC_WS2,1)
I tried doing gcc -E file.h > output.h however this outputs an empty file. I am using MingW compiler.
I expect the defines to be subsituted like this:
#define IOC_IN 0x80000000
#define IOC_WS2 0x08000000
#define _WSAIOW(x,y)(IOC_IN|(x)|(y))
#define SIO_ASSOCIATE_HANDLE (IOC_IN|(IOC_WS2)|(1))
The defines "in the header" are not "expanded", what do you expect to happen?
Instead, places where they are used are going to be preprocessed to have the definitions inserted.
You typically preprocess a C file, not a header, i.e. something where a #define is used without being defined.
GCC (the preprocessor actually) ignores your defines because they are unused. Try appending int x = SIO_ASSOCIATE_HANDLE; and rerun the gcc.

How can I conditionally include a nonexistent file in Visual C++?

I'm using Visual C++ to build and test a project, but I'll also be using it on other platforms. I started the code initially on a different platform.
In some of my class headers I need to include a file which is specific to the other platform, however, this file doesn't exist in the Visual C++ workspace.
I've tried using code like this:
#if TARGET == OTHERPLATFORM
#include "themissingfile.h"
#endif
where TARGET is defined elsewhere as WINDOWS
#define TARGET WINDOWS
However, I still get a compiler error stating that "themissingfile.h" cannot be found. It appears like the precompiler is processing the include before the if. What's the best way to get around this? I suppose I could just create a blank "themissingfile.h" in the Visual C++ source folder, but it seems to me like there should be a better solution.
#define TARGET WINDOWS does not set TARGET to the string WINDOWS: it sets it to whatever WINDOWS is defined to. Most likely this happens:
#define WINDOWS // Sets WINDOWS to 0
#define OTHERPLATFORM // Sets OTHERPLATFORM to 0
#define TARGET WINDOWS // Sets TARGET to 0
#if TARGET == OTHERPLATFORM // Evaluates to 0==0, so true
Use _WIN32 macro instead:
#ifndef _WIN32
#include "themissingfile.h"
#endif
_WIN32 is defined for both 32-bit and 64-bit builds. See C/C++ Predefined Macros.

Passing preprocessor variable to nmake build environment

I am having issues with building driver using nmake on Win7 x64 build environment. I am defining a preprocessor variable and passing it over the command line using -
build /nmake "USER_C_FLAGS=/DMyVersion=3"
And the build log is -
...
/DMyVersion=3
/typedil-
/wd4603
/wd4627
....
So, I clearly see the variable as part of the compiler options. Now in a header fie, I do
#define otherVersion 10
#ifdef MyVersion
#undef otherVersion
#define otherVersion MyVersion
#endif
#define FileVersion otherVersion
The problem is FileVersion is always being 10 irrespective of the MyVersion define passed and exist in the environment. To test, what is going on, I did -
#ifdef MyVersion
#error MyVersion is present in the environment.
#endif
I see the statement being printed. But why is otherVersion is always 10 despite the preprocessor directive is present in the environment ? Why it is not taking the value 3 passed via command line options?
I'm not sure, if this works for you, but some people did try to achieve quite the same using msbuild. They had to adapt the project-file to pipe their definitions "into" the build-process. Have a look at MSBuild.exe not accepting either /p:DefineConstants nor /p:PreprocessorDefinitions

Android NDK Error : No function renaming possible

I've manage to make my Android Project work with a Visual Studio project. The only problem I have is when I compiled the c++ project the vsc++ compiler gives me this error:
android-ndk-r6b\platforms\android-9\arch-x86\usr\include\sys\cdefs.h(252): fatal error C1189: #error : "No function renaming possible"
If anyone had this problem before, any input would be nice.
The error happens when I include the .h that contains includes to those files
jni.h
android\log.h
EDIT :
I've looked at cdefs.h around line 252 :
245 | #if !defined(_STANDALONE) && !defined(_KERNEL)
246 | #ifdef __GNUC__
247 | #define __RENAME(x) ___RENAME(x)
248 | #else
249 | #ifdef __lint__
250 | #define __RENAME(x) __symbolrename(x)
251 | #else
252 | #error "No function renaming possible"
253 | #endif /* __lint__ */
254 | #endif /* __GNUC__ */
255 | #else /* _STANDALONE || _KERNEL */
256 | #define __RENAME(x) no renaming in kernel or standalone environment
257 | #endif
But honestly, I'm not sure what no renaming... means.
It seems that cdefs.h from $(NDKROOT)/.../includes somehow conflicts with the "default" cdefs.h from Visual Studio. Try to directly address the folder android in your includes and change #include <android/log.h> to #include <log.h> in your source file.
Regarding the jni.h I have no further clue...
There is #error pragma in the source code. Find this pragma and explore it's vicinity to check for any comments and / or #ifdef that might give you a hint on what's the problem.
I wanted to do the same thing, compile my Android code using Visual Studio. Even though Studio has no tablet emulator, I could at least run some of my logic under Studio, the parts that do not involve anything specific to Android (e.g. UI drawing). Why bother with such a limited dev environment? Well, simply because Studio has a very nice C++ editor and it compiles much more quickly then Android Studio/Gradle. And I have a lot of non-UI logic that I need to get right. So I'm dividing my work into a pre-step, which I think can be done more quickly with Studio.
For me, I copied over jni.h (from android NDK's x86 folder). And I made some tweaks...
#define __NDK_FPABI__
//#include <sys/cdefs.h>
...
#define JNIEXPORT //gdh: __attribute__ ((visibility ("default")))
That made Studio happy.