0

I have a Project in CMake including a shared library.
To use templated Functions i have to explicitly instantiate or specialize them because the compiler can't know for which types they will be needed from outside the library.
CMake provides a Makro that produces the needed __declspec(dllexport) / __declspec(dllimport)
I have prefixed the declaration of my function with that macro and everything works as intended.

Now when I add a Specialization for that function it strangely also needs that macro,
otherwise the linker creates an error (Ex.: undefined reference to `_imp__Z3addIdET_S0_S0')

There is no immediate Problem here, I am just confused as to why the Specialization needs the declspec again.
Even more so since simple instantiations don't seem to need it.

I am using gcc 10.2.0 from msys2 and ninja 1.10.2 on Windows 10

Edit:

I looked at the other question before asking and it does not answer mine.
When compiling other.cpp the compiler has acces to everything about the template function.
So when compiling main.cpp add<int> is already present in libother.dll and add<double> should be as well. I guess add<double> is only exported into the .dll with __declspec(dllexport)
The question is: why is add<int> exported without it then?

Example:

CMakeLists.txt:

cmake_minimum_required(VERSION 3.19.4)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) #Put Lib and Exe into the same Folder
project(Test)

add_library(Other SHARED other.cpp)
target_include_directories(Other PUBLIC .)

include(GenerateExportHeader)
generate_export_header(Other
                       EXPORT_MACRO_NAME OTHER_EXPORT
                       EXPORT_FILE_NAME OtherExports.h)
target_include_directories(Other PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")

add_executable(Test main.cpp)
target_link_libraries(Test Other)

main.cpp:

#include <iostream>
#include "other.h"
    
int main(){
    std::cout<<add(1,2)<<std::endl;
    std::cout<<add(1.0, 2.0)<<std::endl;
    return 0;
}

other.h:

#include "OtherExports.h"
template<typename T>
OTHER_EXPORT T add(T a, T b);

other.cpp:

#include "other.h"

template<typename T>
T add(T a, T b){
    return a+b;
}

//instantiation for int without declspec macro, compiles!
template int add<int>(int, int);

//specialization for double without declspec macro, doesn't compile!
template <> double add<double>(double a, double b){
    return a-b;
}

//Specialization for double with declspec macro, compiles!
/*template <> OTHER_EXPORT double add<double>(double a, double b){
    return a-b;
}*/
toni714
  • 13
  • 5
  • The declaration of the templated `add()` in other.h uses `OTHER_EXPORT`. The actual template definition in other.cpp therefore needs `OTHER_EXPORT` as well. Without that, the definition of the template will use the standard calling convention - but the declaration does not. The declaration (non-definition) and the definition must match. – Peter Feb 07 '21 at 03:39
  • But the templated definition (first) and the instantiation (second) don't need it. only the specialization (third/fourth). – toni714 Feb 07 '21 at 09:41
  • I edited the question to show why it is not duplicate. @Alan Birtles – toni714 Feb 07 '21 at 10:10
  • Re: " I have to explicitly instantiate or specialize them" -- not usually. The template functions are defined in their respective headers, and the compiler instantiates whichever ones are used, wherever they're used. Sure, if you want to provide a specific set of precompiled instantiations in your library, you can do that, but that's not at all common. – Pete Becker Feb 07 '21 at 15:47
  • @Pete Becker that is true if it were one program. But since the library is compiled independently from the main executable, when compiling the library the compiler has no idea who will maybe load the library later and want which versions of the template. The compilation units(other.cpp) are not "shipped" with the library, so only a dll and include headers, that is why there is no way to somehow add template versions from another compile process (if that even made sense) – toni714 Feb 07 '21 at 17:52
  • @toni714 -- providing compiled code as a library doesn't prevent the compiler from instantiating templates on its own. Templates are defined in header files, and after the compiler has seen that header, it can instantiate any version of the template that it needs. That's how, for example, the standard library implementation works: some of the code is in a library (statically linked, shared, or DLL, doesn't matter), and much of the code is templates that are defined in the standard headers and instantiated when they're used. – Pete Becker Feb 07 '21 at 18:06
  • @Pete Becker as you can see, the template is in this case *not* defined inside the header. feel free to experiment with my example code to see how it behaves – toni714 Feb 07 '21 at 18:27
  • @toni714 -- why isn't defined it in the header? That's how templates are usually managed, and if you're doing something different, you need a strong reason for making your life so much harder. I worked on the standard library at Borland and at Dinkumware (Microsoft ships Dinkumware's library). There are a handful of templates in the standard library that can, for code size reasons, be implemented in the library's DLL. But by far the vast majority of templates are **not** instantiated in the DLL. – Pete Becker Feb 07 '21 at 19:05
  • @Pete Becker i just wanted to see how it would work and found some odd behavior, now i want to know why (if it is intended like that). this is only for learning puropses. i guess a rationale would be if you wanted to keep the implementation 100% secret but that does seem exaggerated. – toni714 Feb 07 '21 at 19:28

0 Answers0