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
    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
    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
    an undefined symbol. */
 
+#ifndef CHECKPANDAVERSION_H
+#define CHECKPANDAVERSION_H
+
 #include "dtoolbase.h"
 
 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