Avoid multiple definitions of `lst::info` (MinGW compile fix)
authorAlexey Sheplyakov <asheplyakov@altlinux.org>
Wed, 6 Jan 2021 07:20:46 +0000 (11:20 +0400)
committerAlexey Sheplyakov <asheplyakov@altlinux.org>
Wed, 6 Jan 2021 13:29:52 +0000 (17:29 +0400)
[55/59] Linking CXX shared library bin/libginac.dll
FAILED: bin/libginac.dll ginac/libginac.dll.a
[skipped long list of object files]
/usr/bin/x86_64-w64-mingw32-ld: ginac/CMakeFiles/ginac.dir/lst.cpp.obj: in function `GiNaC::ptr<GiNaC::basic>::~ptr()':
/home/asheplyakov/work/sw/ginac/_build_w64/../ginac/container.h:150: multiple definition of `GiNaC::container<std::__cxx11::list>::info(unsigned int) const'; ginac/CMakeFiles/ginac.dir/integration_kernel.cpp.obj:/home/asheplyakov/work/sw/ginac/_build_w64/../ginac/container.h:116: first defined here

integration_kernel.cpp makes use of GiNaC::lst without including the `lst.h`
header. That's possible since there's a typedef in `registrar.h` (included
by virtually all GiNaC sources) and a defintion in `container.h` (included
via `add.h` -> `expairseq.h` -> `indexed.h` -> `exprseq.h`), so the compiler
can instantiate container<std::list>. However the explicit specialization
of `lst::info` is not available (in integration_kernel.cpp). This violates
17.8.3.6 [templ.expl.spec] which demands

If a template, a member template or a member of a class template is
explicitly specialized then that specialization shall be declared before
the first use of that specialization that would cause an implicit
instantiation to take place, in every translation unit in which such
a use occurs; no diagnostic is required. If the program does not provide
a definition for an explicit specialization and either the specialization
is used ina way that would cause an implicit instantiation to take place
or the member is a virtual member function, the program is ill-formed,
no diagnostic required.

On ELF platforms libginac appears to link just fine despite having two
instantiations of `lst::info` since the of them (in `integration_kernel.o`)
is a weak symbol:

$ find ginac -type f -name '*.o' | xargs -n 1 nm --print-file-name --defined | c++filt | grep -e 'list>::info('
ginac/CMakeFiles/ginac.dir/integration_kernel.cpp.o:0000000000000000 W GiNaC::container<std::__cxx11::list>::info(unsigned int) const
ginac/CMakeFiles/ginac.dir/lst.cpp.o:0000000000000000 T GiNaC::container<std::__cxx11::list>::info(unsigned int) const

so the linker discards the wrong instantiation of `lst::info` method.
However on MinGW there are no weak symbols (in ELF sense):

$ find ginac -type f -name '*.obj' | xargs -n 1 x86_64-w64-mingw32-nm --print-file-name --defined | c++filt | grep -e 'list>::info('
ginac/CMakeFiles/ginac.dir/lst.cpp.obj:0000000000000010 T GiNaC::container<std::__cxx11::list>::info(unsigned int) const
ginac/CMakeFiles/ginac.dir/integration_kernel.cpp.obj:0000000000000000 T GiNaC::container<std::__cxx11::list>::info(unsigned int) const

Hence the above multiple definition error.

To avoid the problem #include "lst.h" (so explicit specialization is available).
While at it explicitly instantiate lst::info method in lst.cpp

A better solution would be to remove declaration of lst from registrar.h,
but that's too disruptive since GiNaC uses lst a lot: subs, unarchive, etc.

ginac/integration_kernel.h
ginac/lst.cpp
ginac/lst.h

index 1e49338c8b261b01cf31a1be38915c15e89af132..4a8b56582831804a14d291e75178028d69e592a8 100644 (file)
@@ -26,6 +26,7 @@
 #include "basic.h"
 #include "archive.h"
 #include "numeric.h"
+#include "lst.h"
 
 #include <cln/complex.h>
 #include <vector>
index 5ea42bc88728600e7ebf9013e1c833c5d329abe0..9397e351816005d09308a6a17068b43310371912 100644 (file)
@@ -38,6 +38,7 @@ template <> bool lst::info(unsigned inf) const
                return inherited::info(inf);
 }
 
+template bool container<std::list>::info(unsigned) const;
 GINAC_BIND_UNARCHIVER(lst);
 
 } // namespace GiNaC
index b6badee926fbf8c6110ef51c43792cf34bfa667a..8591861c1a252a49690c96e9423af6359bb93301 100644 (file)
@@ -42,6 +42,7 @@ template<> inline char lst::get_close_delim() { return '}'; }
 
 // defined in lst.cpp
 template<> bool lst::info(unsigned inf) const;
+extern template bool container<std::list>::info(unsigned) const;
 GINAC_DECLARE_UNARCHIVER(lst);
 
 } // namespace GiNaC