What I'm talking about is to find a way to avoid the macros in <windows.h> from polluting whatever project I'm writing.
Excerpts from windows.h:
#ifdef UNICODE
#define LoadImage LoadImageW
#else
#define LoadImage LoadImageA
#endif // !UNICODE
#ifdef UNICODE
#define GetMessage GetMessageW
#else
#define GetMessage GetMessageA
#endif // !UNICODE
The majority of macros (over 99%) I'm okay with, but some of them I just couldn't find a way to avoid.
My idea is that since I always qualify the functions calls in my particular framework, e.g. ImageTool::LoadImage, Visual Studio should have enough clue that I'm not referencing the Windows API, which are all in the root namespace, i.e. ::LoadImage. But the MACRO system does not seem to be that smart.
Is there a compiler or preprocessor option that will just enable that?
Macro substitution are basically simple textual replacements, done before the proper compiler even starts to parse the code. Therefore they are not aware of namespaces or any other parts of the C++ syntax above the pure lexical level.
The straight forward way to avoid replacement of your identifiers is to remove the macros:
#ifdef LoadImage
#undef LoadImage
#endif
This of course will also stop following code from accessing the Windows API with the name LoadImage.
You will to live with it - you cannot avoid these macros on Windows platform. There is no macro-name-spacing in C/C++ pre-processor world. You may however, have all of your code defined and implemented before including any windows header - but that's wouldn't be possible, I believe.
Related
According to cplusplus.com, the syntax to define a macro is:
#define identifier replacement
However, I sometimes stumble upon a macro definition which doesn't contain a replacement. For example in afxwin.h, there is the following preprocessor definition:
#define afx_msg // intentional placeholder
My questions:
What happens at compile-time when a preprocessor definition that doesn't have a replacement is used? Is it simply ignored? For example, does the line afx_msg void OnAddButton(); become void OnAddButton();?
What is the purpose of using preprocessor without replacement? Is it simply to make code more clear?
"Nothing" (no text) is a valid replacement text for a macro. It will simply be removed (more precisely, replaced by nothing) by the preprocessor.
There are multiple reasons why you'd use something like this. One is to simply use the macro in #ifdef and similar constructrs.
Another is conditional compilation. A typical use case is public APIs and DLL exports. On Windows, you need to mark a function as exported from a DLL (when building the DLL) or as imported from a DLL (when linking against the DLL). On ELF systems, no such declarations are necessary. Therefore, you'll often see code like this in public library headers:
#ifdef _WIN32
#ifdef BUILDING_MYLIB
#define MYLIB_API __declspec(dllexport)
#else
#define MYLIB_API __declspec(dllimport)
#endif
#else
#define MYLIB_API
#endif
void MYLIB_API myApiFunction();
Yet another reason could be code processing tools. Perhaps you have a tool which parses source code, extracting a list of functions with a certain marker. You can define such a marker as an empty macro.
#define bla
simply defines bla.
you can use it with
#ifdef bla
...
place some code here
...
#endif
a typical use case is #define DEBUG to enable special code parts in debugging mode.
Another way to set such things from "outside" is:
g++ -DDEBUG x.cpp
which also sets the macro DEBUG defined.
And every header file should have something like:
#ifndef THIS_HEADER_INCLUDE_GUARD
#define THIS_HEADER_INCLUDE_GUARD
...
rest of header file
...
#endif
This simply protects your header file for (recursivly) read more the once.
Some can be done with implementation specific #pragma once.
the preprocessor processes it, removing it and replacing it with nothing
could be a variety of reasons, including readability, portability, custom compiler features, etc.
Just caught a silly bug. I have a zip processing library with a CreateFile() function in it. Winbase.h, included somewhere deep in my headers, redefines it as CreateFileW and linker goes nuts.
Of course I will exclude winbase in this particular case. It just shouldn't be in the scope in the first place. But the theoretical question is still interesting,
Is there a way to suppress some defines locally?
You can get around the macro by putting parentheses around the name:
(CreateFile)(arguments);
This works because the macro CreateFile is a function-like macro (i.e. it takes a list of arguments in parentheses); the right parenthesis after the name doesn't match the syntax for using a function-like macro, so the preprocessor does not expand it.
Of course, the "right" solution is to name the function properly, i.e., create_file. <g>
Removing the offending header file is ALWAYS the best solution for this (especially one as large as windows.h or winbase.h - they are included far too freely for my taste in many projects).
The only other solution is #undef offending_symbol.
Of course, another important thing is "do not use names that match the Windows/Linux system call names" - but CreateFile is a very obvious name for a function that creates a file, so I can see the temptation.
Preprocessor macros have no notion of C++ scope. #defines are just text replacements. If you want to have a 'local' #define, you do something like this:
#define CreateFileW CreateFile
... // here I can use the macro
#undef CreateFileW
Or in your case
#undef CreateFileW
... // Here the macro is not available
#define CreateFileW CreateFile
There is
#undef
which removes defines (but nothing else).
Apart from the aforementioned #undef there technically is not much you can do against #defines, at least not portably.
The best way is to not use #define at all, or at least as little as possible and as constrained as possible. Sometimes you just need a macro to generate some boilerplate code a few times. Be sure to #undef that macro once you are done. The only other valid applications of #define I can think of are include guards and flags for conditional preprocessing.
For #define-deseases like the WinAPI headers you just should constrain them as much as possible. Don't use the #defined types of that API in your headers. You almost never want to use an API all over your application, so use it only in the cpps of a small layer around the API. Reducing the dependencies that way gives a lot more than just disinfecting the rest of your code.
This is my first-attempt at writing anything even slightly complicated in C++, I'm attempting to build a shared library that I can interface with from Objective-C, and .NET apps (ok, that part comes later...)
The code I have is -
#ifdef TARGET_OS_MAC
// Mac Includes Here
#endif
#ifdef __linux__
// Linux Includes Here
#error Can't be compiled on Linux yet
#endif
#ifdef _WIN32 || _WIN64
// Windows Includes Here
#error Can't be compiled on Windows yet
#endif
#include <iostream>
using namespace std;
bool probe(){
#ifdef TARGET_OS_MAC
return probe_macosx();
#endif
#ifdef __linux__
return probe_linux();
#endif
#ifdef _WIN32 || _WIN64
return probe_win();
#endif
}
bool probe_win(){
// Windows Probe Code Here
return true;
}
int main(){
return 1;
}
I have a compiler warning, simply untitled: In function ‘bool probe()’:untitled:29: warning: control reaches end of non-void function - but I'd also really appreciate any information or resources people could suggest for how to write this kind of code better....
instead of repeating yourself and writing the same #ifdef .... lines again, again, and again, you're maybe better of declaring the probe() method in a header, and providing three different source files, one for each platform. This also has the benefit that if you add a platform you do not have to modify all of your existing sources, but just add new files. Use your build system to select the appropriate source file.
Example structure:
include/probe.h
src/arch/win32/probe.cpp
src/arch/linux/probe.cpp
src/arch/mac/probe.cpp
The warning is because probe() doesn't return a value. In other words, none of the three #ifdefs matches.
I'll address this specific function:
bool probe() {
#ifdef TARGET_OS_MAC
return probe_macosx();
#elif defined __linux__
return probe_linux();
#elif defined _WIN32 || defined _WIN64
return probe_win();
#else
#error "unknown platform"
#endif
}
Writing it this way, as a chain of if-elif-else, eliminates the error because it's impossible to compile without either a valid return statement or hitting the #error.
(I believe WIN32 is defined for both 32- and 64-bit Windows, but I couldn't tell you definitively without looking it up. That would simplify the code.)
Unfortunately, you can't use #ifdef _WIN32 || _WIN64: see http://codepad.org/3PArXCxo for a sample error message. You can use the special preprocessing-only defined operator, as I did above.
Regarding splitting up platforms according to functions or entire files (as suggested), you may or may not want to do that. It's going to depend on details of your code, such as how much is shared between platforms and what you (or your team) find best to keep functionality in sync, among other issues.
Furthermore, you should handle platform selection in your build system, but this doesn't mean you can't use the preprocessor: use macros conditionally defined (by the makefile or build system) for each platform. In fact, this is the often the most practical solution with templates and inline functions, which makes it more flexible than trying to eliminate the preprocessor. It combines well with the whole-file approach, so you still use that where appropriate.
You might want to have a single config header which translates all the various compiler- and platform-specific macros into well-known and understood macros that you control. Or you could add -DBEAKS_PLAT_LINUX to your compiler command line—through your build system—to define that macro (remember to use a prefix for macro names).
It seems none of TARGET_OS_MAC, __linux__, _WIN32 or _WIN64 is defined at the time you compile your code.
So its like your code was:
bool probe(){
}
That's why the compiler complains about reaching the end of a non-void function. There is no return clause.
Also, for the more general question, here are my guidelines when developping multi-platform/architecure software/libraries:
Avoid specific cases. Try to write code that is OS-agnostic.
When dealing with system specific stuff, try to wrap things into "opaque" classes. As an example, if you are dealing with files (different APIs on Linux and Windows), try to create a File class that will embed all the logic and provide a common interface, whatever the operating system. If some feature is not available on one of the OS, deal with it: if the feature makes no sense for a specific OS, it's often fine to do nothing at all.
In short: the less #ifdef the better. And no matter how portable your code is, test it on every platform before releasing it.
Good luck ;)
The warning is because if none of the defines are actually defined then you have no return in your probe function. The fix for that is put in a default return.
To add something more to this, other than the outstanding options above, the directives __linux__ and _WIN32 are known to the compiler, where the TARGET_OS_MAC directive was not, this can be resolved by using __APPLE__. Source: http://www.winehq.org/pipermail/wine-patches/2003-July/006906.html
I have some C++ code that includes a method called CreateDirectory(). Previously the code only used STL and Boost, but I recently had to include <windows.h> so I could look-up CSIDL_LOCAL_APPDATA.
Now, this code:
filesystem.CreateDirectory(p->Pathname()); // Actually create it...
No longer compiles:
error C2039: 'CreateDirectoryA' : is not a member of ...
Which corresponds to this macro in winbase.h:
#ifdef UNICODE
#define CreateDirectory CreateDirectoryW
#else
#define CreateDirectory CreateDirectoryA
#endif // !UNICODE
The pre-processor is redefining my method call. Is there any possible way to avoid this naming collision? Or do I have to rename my CreateDirectory() method?
You will be better off if you just rename your CreateDirectory method. If you need to use windows APIs, fighting with Windows.h is a losing battle.
Incidently, if you were consistent in including windows.h, this will still be compiling. (although you might have problems in other places).
You could create a module whose sole purpose is to #include <windows.h> and look up CSIDL_LOCAL_APPDATA wrapped in a function.
int get_CSIDL_LOCAL_APPDATA(void)
{
return CSIDL_LOCAL_APPDATA;
}
btw, Well done for working out what happened!
#undef CreateDirectory
As a developer working on a cross platform codebase, this is a problem. The only way to deal with it is to
ensure that windows.h is - on Windows builds at least - universally included. Then the CreateDirectory macro is defined in every one of your compilation units and is universally substituted with CreateDirectoryW. Precompiled headers are ideal for this
OR, if that is an unpleasant proposition, (and it is for me)
isolate windows.h usage into windows specific utility files. Create files that export the basic required functionality. The header files must use data types that are compatible with, but do NOT depend on the inclusion of windows.h. The cpp implementation file must (obviously) use windows.h.
If your utlility functions need to include project header files with conflicting symbols then the following pattern is a necessity:
#include <windows.h>
#ifdef CreateDirectory
#undef CreateDirectory
#endif
// etc
#include "some_class_with_CreateDirectory_method.h"
// ...
You will need to then explicitly call the non macro version of any windows api functions you have #undef'd - CreateDirectoryA or W etc.
push macro, undef it and pop the macro again:
#pragma push_macro("CreateDirectory")
#undef CreateDirectory
void MyClass::CreateDirectory()
{
// ...
}
#pragma pop_macro("CreateDirectory")
You can take a back up of CreateDirectory, then undefine it, and then define it again when you finish your job with you custom one.
#ifdef CreateDirectory
#define CreateDirectory_Backup CreateDirectory
#undef CreateDirectory
#endif
// ...
// Define and use your own CreateDirectory() here.
// ...
#ifdef CreateDirectory_Backup
#define CreateDirectory CreateDirectory_Backup
#undef CreateDirectory_Backup
#endif
Note that name conflict usually comes from a certain header file being included. Until then stuff like CreateDirectory and GetMessage isn't pulled into visibility and code compiles without a problem.
You can isolate such an inclusion into a wrapper header file and "#undef whatever" at its end. Then, whatever name collision you have will be gone. Unless, of course, you need to use those macros in your own code (yeah, so very likely...)
#pragma push_macro("CreateDirectory")
If nothing works, instead of renaming you could use your own namespace for your functions.
I am developing a multi-platform application and in one component I have a class method called DrawText. Unfortunately, I get a linker error (on windows only) saying that there is an unresolved external symbol for a DrawTextW method of this class.
I've seen this before with other methods ending in "Text" where it is looking for either a FooTextW or FooTextA method instead of the FooText method I defined. My assumption is that somewhere in the Windows headers there is a macro definition assigning FooText to FooTextW or FooTextA based on some other definition.
Aside from renaming my function (which is what I did in the past), does anybody have any good ideas for getting around this issue?
Thanks.
Joe
You really only have two choices:
#ifdef DrawText
#undef DrawText
#endif
Or rename your function. Win32 uses macros which have no namespacing or scoping, so you're kinda stuck.
We just re-name our functions.
BTW: It's based on #ifdef UNICODE usually (or _UNICODE or a few other variants).
Yes, this is a real problem with using Windows, and there's no way to turn it off since the headers all look like this:
#ifdef UNICODE
#define GetDlgItemText GetDlgItemTextW
#else
#define GetDlgItemText GetDlgItemTextA
#endif
So you're going to get the symbol defined either way. It would be very nice if you could #define something before #include'ing windows.h that turns this behavior off, but alas none exists.