1

I have a class EventMgr which has a template function to register a listener. But, when I register a listener, linker gives me a "error LNK2019: unresolved external symbol".

Appetizer code:

class EventMgr {

 template< class T, class EvenT>
 void RegisterListener(T* listener, int EventType, void (T::*MemFunc)(EvenT*) );
}

SoundMgr(which is a listener) tries to register for event:

SoundMgr::SoundMgr(void)
{
  EventManager::GetInstance()->RegisterListener(this, 1, (&SoundMgr::handleBulletFired));
}

I'm not sure why it won't link. Why can it not find the reference types ?

brainydexter
  • 19,826
  • 28
  • 77
  • 115
  • Format your code please. – James Jan 21 '11 at 20:00
  • 1
    The linker error should mention the name of the symbol it can not resolve. What is the name of the symbol? Is it RegisterListener? – Maxim Egorushkin Jan 21 '11 at 20:00
  • 1>SoundMgr.obj : error LNK2019: unresolved external symbol "public: void __thiscall EventManager::RegisterListener(class SoundMgr *,int,void (__thiscall SoundMgr::*)(class Event_Bullet_Fired *))" (??$RegisterListener@VSoundMgr@@VEvent_Bullet_Fired@@@EventManager@@QAEXPAVSoundMgr@@HP81@AEXPAVEvent_Bullet_Fired@@@Z@Z) referenced in function "public: __thiscallSoundMgr::SoundMgr(void)" (??0SoundMgr@@QAE@XZ) – brainydexter Jan 21 '11 at 20:09

2 Answers2

5

If you simply declared the template in a .h file and the implementation is instead in a .cpp file then this is the error you will get, because a C++ compiler works one compile unit at a time. When the compiler finds your code calling a template function that has been just declared it will assume that the concrete instantiation will be done by some other compilation unit (there is no way the compiler can know where to find the .cpp file for that function... the compiler only sees one .cpp and all the included .h at a time).

If the template parameters are from a well known list you can simply request the all explicit implementations you know that will be needed for the program in the .cpp.

For example if you have a template function

template<typename T>
T foo(T x)
{
   ...
}

and you know are just going to need int foo(int); and string foo(string); then it's fine to use just the declaration in the .h, provided you also add two lines in the .cpp saying:

template<> int foo(int);
template<> string foo(string);

By doing that you are telling the compiler about what specialization to build. If you later end up using other specializations (e.g. vector<int> foo(vector<int>)) then you must also add this explicit instantiation in the .cpp file of the template.

However in your example looking at the code I guess that you don't know in advance which kind of events will be defined and so this explicit instantiation cannot be done.

The other solution is to simply put the whole template implementation in the .h file instead of separating declaration from implementation. This sometimes can be not trivial because requires you to expose more implementation details probably introducing more dependencies.

6502
  • 112,025
  • 15
  • 165
  • 265
  • I still get the same error, even when I give it an empty body. – brainydexter Jan 21 '11 at 20:21
  • 1
    I tried copying and pasting your code and adding all that was missing, and compiles fine. For sure the code you're compiling is not the one you posted (it's `EventManager` or `EventMgr` ?), so it's quite hard to find where the problem is. – 6502 Jan 21 '11 at 20:35
  • You're correct, it was using a stale copy of module. I clean-and-rebuilt the code (after making sure all the template functions have their body in the header), it worked fine. Thanks! So, as a rule, whenever I use templates, I need to define the body at the time of declaration of the body ? – brainydexter Jan 21 '11 at 20:52
  • 1
    All the template in .h is a common solution, but has the downside that introduces in the clients of your template the dependency on the knowledge of all you need for the implementation (instead of just what you need for the interface). To have a separate template implementation allows to minimize dependencies however requires you to know in advance which specialization you need, and that's not always possible. In theory this interface/implementation separation was the job of `export`ed templates, but just don't go there... – 6502 Jan 21 '11 at 22:33
  • I can imagine the downside you mentioned, since when I stuffed everything into this one header (for EventManager template file), I had to move all my inclusions from my cpp into the header. I am already cringing at this thought, but I don't know the type of events in advance, so I really got no choice, I guess. Thanks a lot for the elaborate answer, it has helped me understand why and when we need things in the header file for templates. – brainydexter Jan 21 '11 at 23:37
1

This probably means that RegisterListener is not actually implemented anywhere. As it is a template function, you should implement it in the header.

Try the following in the header.

template< class T, class EvenT > 
void RegisterListener(T* listener, int EventType, void (T::MemFunc)(EvenT) ) { }
James
  • 5,355
  • 2
  • 18
  • 30
  • I have it implemented in the .cpp file as follows: template< class T, class EvenT> void EventManager::RegisterListener(T* listener, int EventType, void (T::*MemFunc)(EvenT*) ) { lookup[EventType] = new FunctionHandler(listener, memFunc); } Also, I still get the same error if I comment this out and make an empty body in the header – brainydexter Jan 21 '11 at 20:11
  • If you implement it in the .cpp file, it can only be used within that cpp file. Implement in the header. – James Jan 21 '11 at 20:12
  • Even when I give it an empty body in the header, it still gives me that error – brainydexter Jan 21 '11 at 20:19
  • Can you post your `SoundMgr` header in your question? – James Jan 21 '11 at 20:34
  • After refactoring all the template functions to contain their body in the header, and rebuilding the code. It worked! Thanks James – brainydexter Jan 21 '11 at 20:54