2

I have this header:


#pragma once

#if _MSC_VER
#  ifdef mylib_EXPORTS
#    define MYLIB_EXPORT __declspec(dllexport)
#  else
#    define MYLIB_EXPORT __declspec(dllimport)
#  endif
#else
#  ifdef mylib_EXPORTS
#    define MYLIB_EXPORT __attribute__((visibility("default")))
#  else
#    define MYLIB_EXPORT
#  endif
#endif

template<typename T>
class Templ
{
public:
    T getNum() const;
    T getAnotherNum() const;
};

#ifndef mylib_EXPORTS
extern template class MYLIB_EXPORT Templ<int>;
#endif

With this implementation:


#include "mylib.h"

template<typename T>
T Templ<T>::getNum() const
{
    return 7;
}

template<typename T>
T Templ<T>::getAnotherNum() const
{
    return 5;
}

#if 0
template<>
int Templ<int>::getNum() const
{
    return 42;
}
#endif

template class MYLIB_EXPORT Templ<int>;

and this consumer:


#include "mylib.h"

#include <iostream>

int main()
{
    Templ<int> ti;
    std::cout << ti.getNum() << " -- " << ti.getAnotherNum() << std::endl;
    return 0;
}

and the following CMakeLists.txt:

cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
project(cpptest)

if (NOT WIN32)
    set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined")
    set(CMAKE_MODULE_LINKER_FLAGS "-Wl,--no-undefined")
    set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)
    set(CMAKE_CXX_VISIBILITY_PRESET hidden)
endif()

add_compile_options(-Werror)

add_library(mylib SHARED mylib.cpp)

add_executable(myexe main.cpp)
target_link_libraries(myexe PRIVATE mylib)

and if I build and run with MSVC or with GCC, I get the expected output:

7 -- 5

HOWEVER, if I enable the template specialization in mylib.cpp, then I get build failures with GCC:

$ cmake --build .
[1/3] Building CXX object CMakeFiles/mylib.dir/mylib.cpp.o
FAILED: CMakeFiles/mylib.dir/mylib.cpp.o 
/usr/lib/ccache/c++  -Dmylib_EXPORTS  -fPIC -fvisibility=hidden -fvisibility-inlines-hidden   
-Werror -MD -MT CMakeFiles/mylib.dir/mylib.cpp.o -MF CMakeFiles/mylib.dir/mylib.cpp.o.d -o 
CMakeFiles/mylib.dir/mylib.cpp.o -c ../mylib.cpp
../mylib.cpp:24:29: error: type attributes ignored after type is already defined [-Werror=attributes]
 template class MYLIB_EXPORT Templ<int>;
                             ^~~~~~~~~~
cc1plus: all warnings being treated as errors
ninja: build stopped: subcommand failed.

and with MSVC:

LINK Pass 1: command "C:\PROGRA~2\MICROS~3.0\VC\bin\amd64\link.exe /nologo CMakeFiles\myexe.dir
\main.cpp.obj /out:myexe.exe /implib:myexe.lib /pdb:myexe.pdb /version:0.0 /machine:x64 /debug 
/INCREMENTAL /subsystem:console mylib.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib 
ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST 
/MANIFESTFILE:CMakeFiles\myexe.dir/intermediate.manifest CMakeFiles\myexe.dir/manifest.res" failed 
(exit code 1120) with the following output:
main.cpp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: int __cdecl 
Templ<int>::getNum(void)const " (__imp_?getNum@?$Templ@H@@QEBAHXZ) referenced in function main
myexe.exe : fatal error LNK1120: 1 unresolved externals
ninja: build stopped: subcommand failed.

and with clang:

: && /usr/lib/ccache/clang++     CMakeFiles/myexe.dir/main.cpp.o  -o myexe  -Wl,-rpath,/home/stephen
/dev/src/playground/cpp/build libmylib.so && :
CMakeFiles/myexe.dir/main.cpp.o: In function `main':
main.cpp:(.text+0x14): undefined reference to `Templ<int>::getNum() const'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ninja: build stopped: subcommand failed.

What is the correct and portable way to dllexport template instantiations (possibly including template specializations for parts of the template) from a shared library?

steveire
  • 10,694
  • 1
  • 37
  • 48

1 Answers1

-1

You just need MYLIB_EXPORT in front of this line:

int Templ<int>::getNum() const
John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • Doesn't work with GCC. I suggest trying it locally. – steveire Dec 06 '19 at 16:01
  • Amazing downvote for a solution that works on at least one of the compilers mentioned in the question. Are you looking for solutions for all 3? I did try it locally, but I only have Clang (and it works). – John Zwinck Dec 07 '19 at 00:33
  • A solution for MSVC that doesn't work with GCC is not "portable" for C++ use. Maybe you missed that word in the question, I don't know. Thanks for your answer, but it's not upvote-able :). – steveire Dec 09 '19 at 11:16