Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Separate definitions of variadic templates

(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
like image 742
hpm Avatar asked May 01 '15 07:05

hpm


1 Answers

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);
}
like image 183
PiotrNycz Avatar answered Nov 01 '22 21:11

PiotrNycz