C++ Shared Library with Templates: Undefined symbols error
In addition to the other answers, you can explicitly instantiate template classes. This is only useful if you know beforehand what types the template parameters may assume. You instantiate the template with all these types in the library.
For your example to compile, just add the following to the end of shared.cpp:
// Instantiate myclass for the supported template type parameters
template class myclass<int>;
template class myclass<long>;
This instantiates the template with Type=int and places the instantiated code in the shared library. Add as many explicit instantiations as you need, for all the types you need.
Again, if you want to be able to instantiate the template with any arbitrary Type parameter, then you must add the definitions to the header file, so that the compiler knows the source code of the template when instantiating it in other compilation units.
Undefined symbols linker error with simple template class
The template definition (the cpp file in your code) has to be included prior to instantiating a given template class, so you either have to include function definitions in the header, or #include the cpp file prior to using the class (or do explicit instantiations if you have a limited number of them).
Template subclass of non-template base from shared library causing Undefined symbols typeinfo for 'class' link error
It looks like you are building your shared library with "-fvisibility=hidden" and mark exported function with __attribute__((visibility("default")))
however in order to export RTTI you need to mark exported class with __attribute__((visibility("default")))
. From gcc referece:
Note that the type visibility is applied to vague linkage entities associated with the class (vtable, typeinfo node, etc.). In particular, if a class is thrown as an exception in one shared object and caught in another, the class must have default visibility. Otherwise the two shared objects are unable to use the same typeinfo node and exception handling will break.
Shared Library: Undefined Reference with Partial Template Specialization and Explicit Template Instantiation
It turned out to be a false alarm. Nevertheless, this catch took me a couple of hours to finally remember why this symbol might be invisible to consumers. It's really trivial but I feel like posting it here for future visitors who happen to have the same setup. Basically, if you use either a linker script [1] or a (pure) version script [2] (specified with the --version-script
linker option), then don't forget to set global
visibility for those tpl::foo*
third-party-based symbols (or whichever they are in your case). In my case, I originally had the following:
{
global:
extern "C++" {
ml::*;
typeinfo*for?ml::*;
vtable*for?ml::*;
};
local:
extern "C++" {
*;
};
};
what I clearly had to change to
{
global:
extern "C++" {
tpl::foo*;
ml::*;
typeinfo*for?ml::*;
vtable*for?ml::*;
};
local:
extern "C++" {
*;
};
};
in order to link everything properly and get the expected result.
Hope this helps and regards.
BONUS
A curious reader could ask though, "Why the hell are you combining explicit visibility attributes and a linker/version script to control visibility of symbols when there are already the -fvisibility=hidden
and -fvisibility-inlines-hidden
options which are supposed to do just that?".
The answer is that they of course do, and I indeed do use them to build my shared libraries. However, there is one catch. It is a common practice to link some internal libraries (privately) used by your shared library statically (into that library), primarily in order to completely conceal such dependencies (keep in mind, though, that header files accompanying your shared library should also be properly designed to implement this). The benefits are clear: clean and controllable ABI and reduced compile times for shared library consumers.
Take for example, Boost, as the most widespread candidate for such a use case. Encapsulating all of the heavily templated code from Boost privately into your shared library and eliminating any Boost symbols from the ABI will greatly reduce interface pollution and compile times of your shared library consumers, not including the fact that your software components will also look professionally developed.
Anyway, to the point, it turns out that unless those static libraries that you want to link into your shared library were themselves also built with the -fvisibility=hidden
and -fvisibility-inlines-hidden
options (what would be a ridiculous expectation as nobody is going to distribute static libraries with hidden interface symbols by default as it defeats their purpose), their symbols will inevitably still be visible (for instance, through nm -CD <shared-library>
) regardless of the fact that you're building the shared library itself with those options. That is, in this case, you have two options to resolve it:
- Manually rebuild those static libraries (your shared library dependencies) with the
-fvisibility=hidden
and-fvisibility-inlines-hidden
options, what is clearly not always possible/practical given their potential third-party origin. - Use linker/version script (like it is done above) to supply at link time in order to instruct linker to forcefully export/hide proper symbols from your shared library.
Shared library symbol lookup template instantiation
I have confirmed using nm -gDC lib.so that the library contains the symbol and spelled it exactly the same in my lookup attempt but it can't be found.
As Igor Tandetnik correctly commented, the name the library actually exports is the C++
mangled name, and not rosidl_typesupport_cpp::get_service_type_support_handle<example_interfaces::srv::AddTwoInts>
(which is the demangled name).
To see the actual symbol name, use nm -D lib.so
(in particular, omit the -C
flag).
Undefined symbols with extern templates in a static library
Using your github code I can reproduce it with GCC 5.0 or Clang 3.4 but not Clang 3.6 (built from svn).
When it fails Foo.cpp.o
does not contain a definition of qux::Foo<int, 2ul>::Foo(int const&)
$ nm -C Foo.cpp.o
U qux::Foo<int, 2ul>::Foo(int const&)
0000000000000000 W qux::Foo<int, 2ul>::Foo()
U qux::Foo<int, 2ul>::Foo(int const&)
0000000000000000 W qux::Foo<int, 2ul>::Foo()
0000000000000000 W qux::Foo<int, 2ul>::operator[](unsigned long) const
0000000000000000 W std::array<int, 2ul>::operator[](unsigned long) const
0000000000000000 W std::__array_traits<int, 2ul>::_S_ref(int const (&) [2], unsigned long)
But using Clang 3.6 that symbol is defined in
$ nm -C Foo.cpp.o
0000000000000000 W qux::Foo<int, 2ul>::Foo(int const&)
0000000000000000 W qux::Foo<int, 2ul>::Foo()
0000000000000000 W qux::Foo<int, 2ul>::Foo(int const&)
0000000000000000 W qux::Foo<int, 2ul>::Foo()
0000000000000000 n qux::Foo<int, 2ul>::Foo(int const&)
0000000000000000 n qux::Foo<int, 2ul>::Foo()
0000000000000000 W qux::Foo<int, 2ul>::operator[](unsigned long) const
0000000000000000 W std::array<int, 2ul>::operator[](unsigned long) const
0000000000000000 W std::__array_traits<int, 2ul>::_S_ref(int const (&) [2], unsigned long)
0000000000000000 W std::array<int, 2ul>::end()
0000000000000000 W std::array<int, 2ul>::data()
0000000000000000 W std::array<int, 2ul>::begin()
0000000000000000 W int* std::__addressof<int>(int&)
I'm not sure what the problem is, it might be a bug in Clang which has now been fixed, but it's strange that GCC has the same bug.
The function that causes the problem uses C++14's relaxed constexpr rules (looping in a constexpr function), so I assume it's a bug in the compilers' implementation of that new feature.
Undefined reference error for template method
Templated code implementation should never be in a .cpp
file: your compiler has to see them at the same time as it sees the code that calls them (unless you use explicit instantiation to generate the templated object code, but even then .cpp
is the wrong file type to use).
What you need to do is move the implementation to either the header file, or to a file such as VAConfig.t.hpp
, and then #include "VAConfig.t.hpp"
whenever you use any templated member functions.
Undefined reference from shared library in static one
Static libraries are just an agglomeration of object files (their members), perhaps with a ranlib(1) generated index.
On Linux, if you link an object file (3) foo.o
with a static library (2) libee.a
and a shared library (1) libyz.so
and if you pass -rdynamic
at link time (i.e. gcc -rdynamic foo.o libee.a libyz.so -o myprog
or gcc -rdynamic foo.o -lee -lyz -o myprog
) then dynamic linker would resolve the get_object
name at dynamic link time (in ld-linux.so
)
Details are explained in ELF wikipage and Drepper's paper: How To Write Shared Libraries. Read also Levine's book: Linkers and loaders & ld(1) man page.
Related Topics
Reasonably Portable Way to Get Top 64-Bits from 64X64 Bit Multiply
Long VS. Int C/C++ - What's the Point
C++ Get Handle of Open Sockets of a Program
How to Set Given Channel of a Cv::Mat to a Given Value Efficiently Without Changing Other Channels
How to #Include When There Is a Circular Dependency
Why Isn't Memcpy Guaranteed to Be Safe for Non-Pod Types
C++ CSV Line with Commas and Strings Within Double Quotes
Understanding (Simple) C++ Partial Template Specialization
Error When Using In-Class Initialization of Non-Static Data Member and Nested Class Constructor
Is Using an Union in Place of a Cast Well Defined
Using Sendmessage to Send Wm_Close to Another Process
How Does the Stl's Multimap Insert Respect Orderings
Check Xmm Register for All Zeroes
Constexpr Class Taking Const References Not Compiling