7

(Final question at the bottom)

Recently, I asked a question concerning how I would fix linker errors (on duplicate symbols concerning multiple definitions of a template void.

Because I was using the functions in multiple source files, I was suggested to use the keyword inline to allow declarations in the header or put the declarations in a compiled source file.

After I realized that inline had some bad repercussions, I put my declarations in a source file.

Now this was okay, except for variadic templates:

template<typename T, typename... Args>
void cleanup(T *t, Args&&... args);

I found some apparent solutions - but not to variadic templates - use a .tpp file (but it started declaring duplicate symbols again) or keep the source file and add explicit instantiations.

But void cleanup has the potential to be used hundreds of combinations of parameters, so I don't want to explicitly instantiate everything.

Question: So, how would I go about either

  1. Keeping the variadic template definitions in the source file, OR
  2. Putting definitions in a .tpp file without getting duplicate symbols, and ultimately avoid using inline?

Examples of duplicate/undefined symbol errors for .tpp declaration and putting the above template definition in a source file respectively.

duplicate symbol __Z7cleanupI10SDL_WindowJEEvPT_DpOT0_ in:
    CMakeFiles/Game.dir/Game/main.cc.o
    CMakeFiles/Game.dir/Game/RichTools/rtexture.cc.o

_

Undefined symbols for architecture x86_64:
  "void cleanup<SDL_Renderer, SDL_Window*&>(SDL_Renderer*, SDL_Window*&&&)", 
referenced from:
cleanQuit() in main.cpp.o
ld: symbol(s) not found for architecture x86_64
2
  • 1
    You want the compiler to generate instantiations when they are used but you want to automatically discard duplicates? Use inline then! You mention that you considered its use but that it had "bad repercussions", but what exactly are those? Commented May 1, 2015 at 8:10
  • It seems that in your first question you did not understand the answer.The advice to make inline or move to source file was only about your fully specialized functions - these with empty parameter list template <>.
    – PiotrNycz
    Commented May 1, 2015 at 8:24

1 Answer 1

4

The advice in answer to your first question: to make inline or move to source file was only about your fully specialized functions - these with empty parameter list template <>.

So, do that:

Your header file:

// This shall be in header - it is not full specialization:
template<typename T, typename... Args>
void cleanup(T *t, Args&&... args){
    //Cleanup the first item in the list
    cleanup(t);
    //Recurse to clean up the remaining arguments
    cleanup(std::forward<Args>(args)...);
}
// These shall be only declared here, and implemented in source file, 
// treat fully specialized function templates as regular functions
template<>
void cleanup<SDL_Window>(SDL_Window *win);
template<>
void cleanup<SDL_Renderer>(SDL_Renderer *ren);
template<>
void cleanup<SDL_Texture>(SDL_Texture *tex);

Your source file:

template<>
void cleanup<SDL_Window>(SDL_Window *win){
    if (!win){
        return;
    }
    SDL_DestroyWindow(win);
}
template<>
void cleanup<SDL_Renderer>(SDL_Renderer *ren){
    if (!ren){ 
        return;
    }
    SDL_DestroyRenderer(ren);
}
template<>
void cleanup<SDL_Texture>(SDL_Texture *tex){
    if (!tex){
        return;
    }
    SDL_DestroyTexture(tex);
}
template<>
void cleanup<SDL_Surface>(SDL_Surface *surf){
    if (!surf){
        return;
    }
    SDL_FreeSurface(surf);
}
5
  • But that means I would have to use inline for the 'not specialized' function, as I tried this earlier.
    – hpm
    Commented May 1, 2015 at 8:44
  • @Hyperum why do you think so? Just try my advice. For all not fully specialized functions - just they have to be in header files and they can be inline or not inline. C++ linker is removing such duplicates by itself - you do not need to worry.
    – PiotrNycz
    Commented May 1, 2015 at 8:54
  • @Hyperium - there is also explicit template instantiation way - but as you already observed - it is very unconvienent to use...
    – PiotrNycz
    Commented May 1, 2015 at 8:57
  • Okay, well, I guess it worked. Right now, I am so confused, because I tried it this way before once but I had to use the keyword inline because it was giving me duplicate symbols - so I must have done something wrong. Thanks anyways.
    – hpm
    Commented May 1, 2015 at 8:59
  • 1
    @Hyperum your linker complained only about fully specialized functions: _ZN5RUtil7cleanupI10SDL_WindowJEEEvPT_DpOT0 is void RUtil::cleanup<SDL_Window>(SDL_Window*). Use demangler.com to get demangled versions of your linker symbols...
    – PiotrNycz
    Commented May 1, 2015 at 9:04

Not the answer you're looking for? Browse other questions tagged or ask your own question.