I create a shared library and use default symbol visibility "hidden", i.e., I explicitly have to modify visibility of anything that shall be exported. Moreover, I defined templates in the header files and explicitly instantiate them in the source files of the library to enforce compilation (and to hide the implementation from the library users). For this header file foo.hpp
#include <gmpxx.h>template <typename Number>__attribute__((visibility("default")))Number add(const Number& a, const Number& b);
and this source file foo.cpp
#include "foo.hpp"template <typename Number>Number add(const Number& a, const Number& b){return a + b;}template double add(const double& a, const double& b);template int add(const int& a, const int& b);template mpq_class add(const mpq_class& a, const mpq_class& b);
the compilation with
g++ -fPIC -fvisibility=hidden -c foo.cppg++ -fPIC -shared -o libfoo.so foo.o
gives the following (relevant) symbols (obtained via nm -CD libfoo.so
):
__gmpq_add__gmpq_initdouble add<double>(double const&, double const&)int add<int>(int const&, int const&)
However, I would have expected a symbol for the mpq_class
implementation as well. Of course, if I now link to this library and try to call add<mpq_class>
then I get an undefined symbol error. I can enforce exporting by adding an explicit instantiation for mpq_class
in foo.cpp
that is explicitly marked for export:
template <>__attribute__((visibility("default")))mpq_class add(const mpq_class& a, const mpq_class& b){return a + b;}
Of course, this does not make much sense since I duplicate code or I have to forward to a joint implementation for each such function. For template classes, this seems to only happen for the contructors and destructors, but not for members.
I wonder why this happens. I would guess that this is somehow because mpq_class itself is a template class, but I find it strange.
Best Answer
This thread may be a zombie, but I was researching this problem and figured this information may help somebody else!
I started with this code:
In the header file:
#ifdef MY_LIB_STATIC# define MY_LIB_API#elif defined(WIN32)# ifdef MY_LIB_EXPORTS# define MY_LIB_API __declspec(dllexport)# else# define MY_LIB_API __declspec(dllimport)# endif#else# ifdef MY_LIB_EXPORTS# define MY_LIB_API __attribute__((visibility("default")))# else# define MY_LIB_API# endif#endiftemplate <class T>class Container{public:size_type size() const;};
In a C++ file:
template <class T>size_t Container<T>::size() const{return m_impl->size();}template class MY_LIB_API Container<Port*>; // Port is a class
I then use call size() from a client, MySourceFile.cpp.
This works fine on Windows/Visual Studio, but when using -fvisibility=hidden on Linux/gcc, it gives an error like:
ld: MySourceFile.cpp:115: undefined reference to `Container<Port*>::size() const'
The solution is to put MY_LIB_API on every declaration, so in the header:
template <class T>class MY_LIB_API Container{public:size_type size() const;};
My guess:
- The default visibility is hidden.
- If there is no visibility attribute on a class, it uses the automatic setting of hidden.
- Now, there are two competing class declarations, and the one that says "default" loses, so it becomes hidden.
The use of the word "default" to mean non-hidden, rather than to mean to use the default specified on the command line, makes this all a bit obtuse unfortunately.