You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
10 KiB
215 lines
10 KiB
2 years ago
|
As of December 15, 2004 VTK no longer includes windows.h by default in
|
||
|
most of its header files. This change results in a 70% compile time
|
||
|
reduction on Windows compilers because the compiler does not have to
|
||
|
parse the 280K lines of source the preprocessor produces from
|
||
|
windows.h. All VTK code that still includes windows.h does so through
|
||
|
vtkWindows.h which includes a minimal part of the real windows.h
|
||
|
header. This also avoids contaminating user code with the windows.h
|
||
|
API just because it included a VTK header.
|
||
|
|
||
|
The main problem with windows.h is what we call the "windows mangling"
|
||
|
problem. In order to support UNICODE and ASCII characters APIs,
|
||
|
windows.h defines most of its API as macros. This code appears in the
|
||
|
windows header:
|
||
|
|
||
|
WINUSERAPI int WINAPI GetClassNameA(HWND hWnd, LPSTR lpClassName,
|
||
|
int nMaxCount);
|
||
|
WINUSERAPI int WINAPI GetClassNameW(HWND hWnd, LPWSTR lpClassName,
|
||
|
int nMaxCount);
|
||
|
#ifdef UNICODE
|
||
|
#define GetClassName GetClassNameW
|
||
|
#else
|
||
|
#define GetClassName GetClassNameA
|
||
|
#endif // !UNICODE
|
||
|
|
||
|
The idea for windows.h is that user code can simply call GetClassName
|
||
|
and automatically support both UNICODE and ASCII character sets based
|
||
|
on a preprocessor switch. The problem for everyone else is that the
|
||
|
windows API does not have any prefix on its functions, and the
|
||
|
preprocessor has no notion of namespaces or classes. Therefore when
|
||
|
windows.h is included it invisibly renames methods in VTK like
|
||
|
GetClassName to another name. Examples of other mangled names include
|
||
|
GetObject, SetProp, GetProp, RemoveProp, and CreateDirectory. In fact
|
||
|
there are over 500 such names in the windows API.
|
||
|
|
||
|
Now consider what happens in each of these cases:
|
||
|
|
||
|
1.) VTK includes windows.h everywhere (as it did previously). In this
|
||
|
case the methods are always renamed for all VTK code and all user
|
||
|
code that includes a VTK header. The method GetClassNameA is
|
||
|
actually visible as an export from the DLL. Since the renaming is
|
||
|
consistent then user code links correctly to VTK libraries.
|
||
|
|
||
|
However, applications and other libraries do not necessarily
|
||
|
include VTK or windows.h in all of their sources. When this
|
||
|
outside code uses methods that are mangled by windows.h then they
|
||
|
get mangled in some translation units but not others. This
|
||
|
results in linking errors within the outside application for no
|
||
|
other reason than that it included a VTK header!
|
||
|
|
||
|
Also, when VTK is built without UNICODE defined then all the
|
||
|
methods are renamed to have "A" at the end. If the user
|
||
|
application defines UNICODE and includes a VTK header then the
|
||
|
methods get renamed to have "W" at the end, which results in
|
||
|
unresolved symbols like "GetClassNameW". Then the user complains
|
||
|
and is told he/she has to rebuild VTK from scratch with UNICODE
|
||
|
defined. Basically VTK must be built separately for ASCII and
|
||
|
UNICODE applications.
|
||
|
|
||
|
2.) VTK does not include windows.h everywhere. In this case the
|
||
|
methods are not renamed when VTK is built. The method
|
||
|
GetClassName is visible as an export from the DLL. User code that
|
||
|
includes windows.h before a VTK header will cause the method to be
|
||
|
renamed in the user's translation units. This will cause
|
||
|
unresolved symbols with names like "GetClassNameA" or
|
||
|
"GetClassNameW".
|
||
|
|
||
|
The solution to this problem is to start by NOT including windows.h
|
||
|
everywhere. This avoids all the problems listed in case 1 above. In
|
||
|
order to avoid the problems listed in case 2, we rename all affected
|
||
|
methods to have names that do not get mangled. For example,
|
||
|
GetClassName becomes GetNameOfClass. In order to maintain
|
||
|
compatibility with application code, the old names of the methods must
|
||
|
still be provided. We now export all three possible names of the
|
||
|
original method from the library. In the case of GetClassName, the
|
||
|
names "GetClassName", "GetClassNameA", and "GetClassNameW" are all
|
||
|
provided as methods in the class and exported from the library. This
|
||
|
way when user code calls the method it does not matter which name it
|
||
|
gets because all three names are available.
|
||
|
|
||
|
Here is how all three names can be provided in a VTK header regardless
|
||
|
of whether windows.h has been included before it:
|
||
|
|
||
|
// See vtkWin32Header.h for definition of VTK_WORKAROUND_WINDOWS_MANGLE.
|
||
|
#ifdef VTK_WORKAROUND_WINDOWS_MANGLE
|
||
|
# define GetClassNameA GetClassName
|
||
|
# define GetClassNameW GetClassName
|
||
|
#endif
|
||
|
const char* GetClassName() const;
|
||
|
#ifdef VTK_WORKAROUND_WINDOWS_MANGLE
|
||
|
# undef GetClassNameW
|
||
|
# undef GetClassNameA
|
||
|
//BTX
|
||
|
const char* GetClassNameA() const;
|
||
|
const char* GetClassNameW() const;
|
||
|
//ETX
|
||
|
#endif
|
||
|
|
||
|
The method GetClassName has three cases. If windows.h is not included
|
||
|
then it is not mangled and the name is provided. If windows.h is
|
||
|
included and UNICODE is not defined then GetClassName gets mangled to
|
||
|
GetClassNameA, but then gets replaced by GetClassName again. The
|
||
|
preprocessor will not recursively expand a macro, so replacement stops
|
||
|
there and the GetClassName method is declared. When UNICODE is
|
||
|
defined the same process occurs but through GetClassNameW instead.
|
||
|
The methods GetClassNameA and GetClassNameW are not mangled so they
|
||
|
can be provided directly. They are surrounded by a BTX/ETX pair
|
||
|
because they should not be wrapped since scripting language code will
|
||
|
not be mangled by windows.h.
|
||
|
|
||
|
Now that all three names are provided we can address the fact that
|
||
|
GetClassName is supposed to be a virtual function. When a subclass
|
||
|
wants to override a method with a mangled name it has to use this same
|
||
|
trick to override all three names. Existing user code that tries to
|
||
|
override the function will only replace one of the three names, and it
|
||
|
will not implement the new unmangled name at all. It is not possible
|
||
|
to get this code to work out-of-the-box, but we can at least produce a
|
||
|
compiler error to get the user's attention. We change the signature
|
||
|
of the three possible mangled names to:
|
||
|
|
||
|
virtual const char* const GetClassName() const;
|
||
|
virtual const char* const GetClassNameA() const;
|
||
|
virtual const char* const GetClassNameW() const;
|
||
|
|
||
|
and provide the new name with the original signature:
|
||
|
|
||
|
virtual const char* GetNameOfClass() const;
|
||
|
|
||
|
When existing code tries to override the original method it will get a
|
||
|
compiler error that the return type does not match due to the extra
|
||
|
"const" qualifier. This qualifier is otherwise meaningless and does
|
||
|
not affect calls to the method. When the user encounters the error
|
||
|
and reads the source with the new signature, it will be clear that the
|
||
|
method is deprecated and that the replacement is called
|
||
|
GetNameOfClass. The user code can then be modified to override the
|
||
|
new method name.
|
||
|
|
||
|
Users can detect places in their own code that may need modification
|
||
|
by using this cmake script:
|
||
|
|
||
|
VTK/Utilities/Upgrading/FindWindowsMangledMethods.cmake
|
||
|
|
||
|
There are three backward-compatibility issues:
|
||
|
|
||
|
1.) User code that used the windows API without including windows.h
|
||
|
that worked before because VTK included it will now break until
|
||
|
the explicit inclusion is added. This is considered acceptable
|
||
|
because the code was technically wrong in the first place. As a
|
||
|
quick-fix, users can define VTK_INCLUDE_WINDOWS_H in their
|
||
|
application and VTK will include windows.h as it did before.
|
||
|
|
||
|
2.) The virtual methods that have been renamed and replaced as
|
||
|
described above must be renamed in user code.
|
||
|
|
||
|
3.) All functions that fall victim to mangling have been deprecated.
|
||
|
User code will work (with warnings) but should be modified to call
|
||
|
the new non-mangled versions of the methods. VTK 5.0 includes
|
||
|
support for the original names but it will be removed in a future
|
||
|
version. Use the VTK_LEGACY_REMOVE setting when building VTK to
|
||
|
help make sure your application can build without using deprecated
|
||
|
code.
|
||
|
|
||
|
Frequently Proposed Alternatives:
|
||
|
|
||
|
Several people have proposed alternatives that they think solve the
|
||
|
problem with less of a backward compatibility problem. Here are some
|
||
|
of the common proposals and the reasons they were rejected:
|
||
|
|
||
|
1.) Use #undef to avoid the name mangling altogether.
|
||
|
|
||
|
If VTK includes windows.h and then does the #undef then user code
|
||
|
will not be able to access the windows API through the standard
|
||
|
means. If VTK does not include windows.h and uses #undef just in
|
||
|
case the user included windows.h first then user code can still
|
||
|
include windows.h after the VTK header and then their calls to VTK
|
||
|
methods will be mangled and will not compile.
|
||
|
|
||
|
2.) Do not include windows.h but instead provide the same mangled
|
||
|
names by defining the macros in VTK the same way windows.h does.
|
||
|
|
||
|
The idea behind this solution is that the compile time improvement
|
||
|
is achieved without breaking the previous mangling behavior. This
|
||
|
solution does not address the problems when users build VTK
|
||
|
without UNICODE and then build their application with UNICODE. It
|
||
|
defines macros in VTK that are supposed to be defined in a system
|
||
|
header. This is always dangerous. The solution used above does
|
||
|
not every actually change or redefine any macros defined by
|
||
|
windows.h. It just temporarily defines extra macros.
|
||
|
|
||
|
3.) Use the above solution but change VTK_INCLUDE_WINDOWS_H to a
|
||
|
VTK_DO_NOT_INCLUDE_WINDOWS_H so that the previous default behavior
|
||
|
of including windows.h is preserved for user applications. VTK
|
||
|
can define VTK_DO_NOT_INCLUDE_WINDOWS_H when it is building
|
||
|
itself.
|
||
|
|
||
|
This helps existing applications but will also allow new
|
||
|
applications to be written that do not include windows.h properly.
|
||
|
It will also prevent the compile-time improvements from
|
||
|
propagating to application code by default. The policy we are
|
||
|
trying to achieve is that including a VTK header should not do
|
||
|
anything but define VTK... and vtk... symbols to avoid namespacing
|
||
|
violations. We are willing to let users break this policy by
|
||
|
defining macros but we do not want to require users to define
|
||
|
macros to get this policy.
|
||
|
|
||
|
NOTE: Since GetClassName is so widely used it was decided that it
|
||
|
would not be renamed or deprecated at this time. The documentation
|
||
|
above uses GetClassName as an example but not all of these changes
|
||
|
were actually applied to it in VTK. All changes were applied to other
|
||
|
offending methods, but only enough changes were applied to
|
||
|
GetClassName to get it to work whether or not windows.h is included or
|
||
|
UNICODE is defined. The method is no longer virtual so user code must
|
||
|
define a GetClassNameInternal protected method instead of GetClassName
|
||
|
in order to override it. Since most user code defines the method with
|
||
|
vtkTypeRevisionMacro anyway this should not require many changes.
|