Browse Source

CMake: Enhance checkPandaVersion.h

This removes the requirement that it only be included
in a single C++ file per library, by forcing the compiler
to emit the function that references the version symbol
as a weak symbol itself.  Now, the linker will not only
tolerate redundant inclusions, it will also coalesce them
together.
Sam Edwards 7 years ago
parent
commit
d96765b957
1 changed files with 32 additions and 7 deletions
  1. 32 7
      dtool/src/dtoolbase/checkPandaVersion.h.in

+ 32 - 7
dtool/src/dtoolbase/checkPandaVersion.h.in

@@ -18,7 +18,7 @@
 
 
 /* Include this file in code that compiles with Panda to guarantee
 /* Include this file in code that compiles with Panda to guarantee
    that it is linking with the same version of the Panda DLL's that it
    that it is linking with the same version of the Panda DLL's that it
-   was compiled with.  You should include it in one .cxx file only. */
+   was compiled with. */
 
 
 /* We guarantee this by defining an external symbol which is based on
 /* We guarantee this by defining an external symbol which is based on
    the version number.  If that symbol is defined, then our DLL's
    the version number.  If that symbol is defined, then our DLL's
@@ -26,14 +26,39 @@
    DLL; but the system linker will prevent the DLL from loading with
    DLL; but the system linker will prevent the DLL from loading with
    an undefined symbol. */
    an undefined symbol. */
 
 
+#ifndef CHECKPANDAVERSION_H
+#define CHECKPANDAVERSION_H
+
 #include "dtoolbase.h"
 #include "dtoolbase.h"
 
 
 extern EXPCL_DTOOL_DTOOLBASE int @PANDA_VERSION_SYMBOL@;
 extern EXPCL_DTOOL_DTOOLBASE int @PANDA_VERSION_SYMBOL@;
 
 
-#ifndef WIN32
-/* For Windows, exporting the symbol from the DLL is sufficient; the
-   DLL will not load unless all expected public symbols are defined.
-   Other systems may not mind if the symbol is absent unless we
-   explictly write code that references it. */
-static int check_panda_version = @PANDA_VERSION_SYMBOL@;
+/* Just declaring the symbol isn't good enough.  We need to force the
+   compiler and linker to preserve the external reference long enough
+   to end up in the output DLL.  Therefore, we have to reference that
+   symbol somehow.
+
+   Forcing the compiler to include a reference in its output object
+   file is easy enough: just define a function that makes use of it
+   in some way.  The problem is the linker, which will enforce the
+   C++ One-Definition Rule and get upset about said definition
+   appearing in multiple places in the program.  We can appease the
+   linker by forcing the compiler to emit a weak symbol.  Many
+   compilers have syntax to request this explicitly, but since it
+   varies from compiler to compiler, that wouldn't be very portable.
+
+   Fortunately, the C++ ODR itself has some exceptions, where a
+   definition can occur in multiple translation units *if and only if*
+   it's the same definition each time.  In these cases, the compiler
+   must emit a weak symbol, because the ODR does not guarantee that
+   the same definition isn't repeated in any other translation units.
+   One such exception is template instantiation, which we use thus: */
+template<typename T>
+class CheckPandaVersion {
+public:
+  int check() { return @PANDA_VERSION_SYMBOL@; }
+};
+
+template class CheckPandaVersion<void>;
+
 #endif
 #endif