Splitting a template and class into definition and declaration.

Unimatrix_001
Unimatrix_001 used Ask the Experts™
on
Hi,

I have the code below, which is all well and good but I'd like to move the definition of the setListener method to the cpp file, yet I seem to be having difficulty doing this as I get complains about the template needing arguments?

Thanks,
Uni
template<typename TEventHandlerClass>
class CEventRaiser{
public:
	typedef void (TEventHandlerClass::*TEventHandlerMethod)();
	void setListener(TEventHandlerClass *aEventHandlerClass, TEventHandlerMethod aEventHandlerMethod){
		eventHandlerClass=aEventHandlerClass;
		eventHandlerMethod=aEventHandlerMethod;
	}
};

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
evilrixSenior Software Engineer (Avast)

Commented:
You again? :-p

Author

Commented:
Hehehe, afraid so. ;) Still... at least these are simpler and less awkward. :)
Senior Software Engineer (Avast)
Commented:
>> I'd like to move the definition of the setListener method to the cpp file
No can do.

The problem is the template parser, when it instantiates the template, needs to see the full template definition (including this member) and if it can't the template can't be instantiated. The actual error you are seeing isn't actually related to this BTW but even if I show you (below) the correct out-of-class definition syntax it still won't help you -- you'll just get linker errors.

The C++ standard does define the "export" keyword to try and resolve this issue... no compiler I know supports it!

http://www.parashift.com/c++-faq-lite/templates.html#faq-35.14
http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=52


template <typename TEventHandlerClass>
void CEventRaiser<TEventHandlerClass>::setListener(TEventHandlerClass *aEventHandlerClass, TEventHandlerMethod aEventHandlerMethod)
{
   eventHandlerClass=aEventHandlerClass;
   eventHandlerMethod=aEventHandlerMethod;
}

Open in new window

CompTIA Network+

Prepare for the CompTIA Network+ exam by learning how to troubleshoot, configure, and manage both wired and wireless networks.

Author

Commented:
Shame... Oh well, thanks anyways. :)
evilrixSenior Software Engineer (Avast)

Commented:
BTW: Specialization CAN go in the .cpp file (because they are concrete instantiations).

The rules and semantics of templates can be oh so painful sometimes :)

This is a REALLY good book to get your hands on if you wanna learn all about templates.
http://www.josuttis.com/tmplbook/

Author

Commented:
Well, my knowledge if templates is a little weak (read non-existent) so it shouldn't hurt to get hold of it... Clearly it hasn't done you any harm! ;)
jkr
Top Expert 2012

Commented:
Thinking along the lines of th following?
template<typename TEventHandlerClass>
class CEventRaiser{
public:
        typedef void (TEventHandlerClass::*TEventHandlerMethod)();
        void setListener(TEventHandlerClass *aEventHandlerClass, TEventHandlerMethod aEventHandlerMethod);
};
 
template<typename TEventHandlerClass>
void CEventRaiser<TEventHandlerClass>::setListener(TEventHandlerClass *aEventHandlerClass, TEventHandlerMethod aEventHandlerMethod){
                eventHandlerClass=aEventHandlerClass;
                eventHandlerMethod=aEventHandlerMethod;
        }

Open in new window

Author

Commented:
Oh! It works! :)

Author

Commented:
evilrix, would you mind if I got this question reopened as it seems jkr has come up with a way to do this?

Thanks,
Uni
evilrixSenior Software Engineer (Avast)

Commented:
>> The problem is the template parser, when it instantiates the template, needs to see the full template definition
This is (the quick explanation of) why...

Templates are instantiated in 2 passes.

1. The point of declaration. (PoD)

At this point the template compiler checks the syntax of the template but does NOT consider the dependent types (the template parameters).

So consider the following template code...

template <typename T>
void foo(T const & t)
{
   t.bar();
}

This is syntactically sound; however, at this point we have no idea what type the dependent type (T) is so we just assume that in all cases of T it is correct to call member bar() on it. Of course, if type T doesn't have this member then we have a problem but until we know what type T is we don't know that it is so this code is ok for the 1st pass.

NB. it is perfectly correct to define a template that won't be corrected under all circumstances of instantiation. Unless it is actually instantiated it never actually exists so the compiler will not complain.

2. Point of instantiation (PoI)

This is the point where we actually define a concrete type of our template. At this point the syntax is checked against the known dependent type. To do this the compiler must be able to see the full definition of the template to check it. If it was defined in a .cpp file (aka translation unit) and this definition was not in the same one as where the template is instantiated the compiler has no way to check this, hence it needs to be put somewhere that is visible to all PoIs and that is, of course, in a header.

So consider these 2 concrete instantiations...

foo(1); // this will fail the 2nd pass because an int (1 is an int) does not have a member function called bar()
foo(b); // Assuming b has a member function called bar this instantiation is fine


I hope that, in some small way, makes a little sense :)
evilrixSenior Software Engineer (Avast)

Commented:
>> evilrix, would you mind if I got this question reopened as it seems jkr has come up with a way to do this?
It's no different from what I posted already.

That is the correct way to define the function out of the class but it cannot go in the .cpp file for the reasons I've just given above.

Author

Commented:
>>I hope that, in some small way, makes a little sense :)
Perfectly! :) Although I'm curious to why jkr's proposal works fine? :S
evilrixSenior Software Engineer (Avast)

Commented:
>> That is the correct way to define the function out of the class but it cannot go in the .cpp file for the reasons I've just given above.
BTW: This will only manifest as a problem when you start using multiple .cpp files... as all but trivial projects will.
evilrixSenior Software Engineer (Avast)

Commented:
>> Perfectly! :) Although I'm curious to why jkr's proposal works fine? :S
It is not a syntax issue... it's a visibility issue. If the template definition is in translation unit A and you try to instantiate it in translation unit B (a translation unit is just a fancy name for a .cpp file with all things included) the template compiler will not be able to instantiate the template because it can't see the full definition so it will result in linker errors. If everything is in one place then it will work... but it is not a safe way to write templates, because sooner or later you'll mix them over translation units and all hell will break loose. The simple rule is to define templates in headers (unless your compiler just happens to support the export keyword, like I said I know on none that do).

Author

Commented:
>>...but it cannot go in the .cpp file for the reasons I've just given above.
Hm, I have it split between a header and cpp file okay? I've included some code that is using it, yet this compiles fine? This is confusing... Silly me thinking this is simpler/less awkward...  :(
.h file:
-----------------------------------------------------
template<typename TEventHandlerClass>
class CTimer{
	public:
		void initialiseTimer(TEventHandlerClass *aEventHandlerClass);
	private:
		TEventHandlerClass	*eventHandlerClass;
};
 
 
.cpp file:
-----------------------------------------------------
template<typename TEventHandlerClass>
void CTimer<TEventHandlerClass>::initialiseTimer(TEventHandlerClass *aEventHandlerClass){
	eventHandlerClass=aEventHandlerClass;
}

Open in new window

Author

Commented:
Ah hold up! I've worked out what you mean now. It compiles fine as it stands, but as soon as I try to call the initialiseTimer method it throws a little tizzy.... Ordering that template book as we speak. ;)

Cheers,
Uni.
evilrixSenior Software Engineer (Avast)

Commented:
>> It compiles fine as it stands, but as soon as I try to call the initialiseTimer method it throws a little tizzy....
Bingo :) -- sorry was writing you an example and got lost in time!

If template is declared in header A and defined in .cpp A and you try to instantiate it in .cpp B it ain't gonna work. In the real work it is highly unlikely (although not improbable) that's you'd go to all the effort of creating a generic template class/function in a header that you'll only ever use in one translation unit. This being the case the only real solution is to leave the code in the header. There are other solutions but they are actually less desirable than leaving the code in the header.

On the rare occasion that your template class/function is only going into 1 translation unit then the definition can go in there too but in that case its probably better to declare/define the template completely in the translation unit, in an unnamed namespace, so it will have no external linkage at all, to avoid the problems I discuss above.

// My .cpp with a template I never plan to use elsewhere
namespace
{
   template <typename T>
   struct LocalUseOnly
   {
      void foo();
   }

   template < typename T>
   void LocalUseOnly<T>::foo()
   {
   }
}

But, as you can see, it is now a moot point whether you define the member function in or out of the class anyway since it's still all in one place :) Ergo, just leave it in the class. The exception is, as I already said before, specializations. These MUST be outside the class (since they are concrete instantiations and cannot go in the class body... this is the C++ law! Um, just to note, Visual Studio doesn't enforce this... it is wrong, the standard clearly states they MUST go outside the body) and to avoid breaking the One Definition Rule {http://en.wikipedia.org/wiki/One_Definition_Rule} you will either need to make then inline or put them in the .cpp file.

Like I said above, "The rules and semantics of templates can be oh so painful sometimes".

I hope I've managed to explain it better this time around... don't worry if your head tingles now... I've been doing meta-template programming for quite a few years now and I still get a headache sometimes (actually often!) :)
// cat foo.h -------------------------------------------------->
 
#ifndef Y_H_
#define Y_H_
 
template <typename T>
class Foo
{
public:
	void func_bad(T const & t); // declared
	void func_good(T const & t){} // defined
};
 
#endif
 
// cat foo.cpp -------------------------------------------------->
 
#include "foo.h"
 
template <typename T>
void Foo<T>::func_bad(T const & t)
{
}
 
// cat x.cpp -------------------------------------------------->
 
#include "foo.h"
 
int main()
{
	Foo<int> foo;
	foo.func_good(1);
	foo.func_bad(1);
}
 
// ---------------------------------------------------------------------------
 
/tmp/cclxMVTa.o: In function `main':
x.cpp:(.text+0x3f): undefined reference to `Foo<int>::func_bad(int const&)'
collect2: ld returned 1 exit status

Open in new window

Author

Commented:
Hehehe, that's a lecture not an example! ;) It's covered quite a bit of ground that I've just been searching for. You should adapt that into one of your articles! ;) That's brilliant, thanks very much. :)

Found a hacky way of doing it - not at all credible, but place the the definitions in a seperate file and then include that in the header:

CTimerTemplateDefinitions.cpp:
#define void_CTimer_initialiseTimer \
      typedef void (TEventHandlerClass::*TEventHandlerMethod)(); \
      void CTimer<TEventHandlerClass>::initialiseTimer(TEventHandlerMethod aEventHandlerMethod){ \
      eventHandlerMethod=aEventHandlerMethod; \
}

CTimer:
class CTimer{
...
public:
      void_CTimer_initialiseTimer
...
...
};

Not exactly wonderful, but at least it is a (very!) crude visual seperation of declaration and definition. ;)
evilrixSenior Software Engineer (Avast)

Commented:
>> that's a lecture not an example
Oops... I was aiming to be helpful -- I hope it didn't come over in any other way :S

>> You should adapt that into one of your articles! ;)
Funny, I am currently trying to think of what to write next. I was gonna do a beginners guide to STL containers but maybe a little bit of meta-template magic would also be nice :)

>> That's brilliant, thanks very much
Uri, you are always very welcome.

>> Found a hacky way of doing it - not at all credible, but place the the definitions in a seperate file and then include that in the header:
Yeah, that's one of the "other solutions but they are actually less desirable than leaving the code in the header" ways I was eluding too (but decided to avoid telling you about to save youfrom pain of a high order). My issue with that is that cpp files are not designed to be included; they have no include guards for a start and often contains "using namspace" statements that will cause namespace pollution. This will come bite you big in the end :)

>> visual seperation of declaration and definition
Yeah, it's the think I dislike most about templates but due to limitations of compilers/linkers it's the way it has to be. I don't think C++0X improves this any either (although we do get concepts... ooooh I am so excited!).
evilrixSenior Software Engineer (Avast)

Commented:
>> My issue with that is that cpp files are not designed to be included;
Of course, you can just have 2 headers... 1 for declaration and 1 for definition but I just find that even more confusion and it becomes hard to manage large projects. YMMV :)

Author

Commented:
>>Oops... I was aiming to be helpful -- I hope it didn't come over in any other way :S
Not at all!!! I didn't mean it in anyway other than you put a lot of (appreciated) effort into it, it certainly wasn't intended to come across as being a jab at you!

>>This will come bite you big in the end :)
Point taken - into the header they go (and stay).

Shame really - as it seems that the preprocessor could easily take care of this - granted it would be an unofficial extension but MS are accustomed to taking liberties so it won't be much of a stretch for them. Chances are GCC wouldn't be far behind being open source etc.

I'm quite intrigued at this whole C++0x release - I'll have to take a closer look... :)

Uni.
evilrixSenior Software Engineer (Avast)

Commented:
>> Point taken - into the header they go (and stay).
You can still separate them all in the same header.

// First class declaration
template <typename T>
  struct CTimer
  {
     void foo();
  }

// Followed by function definitions
  template < typename T>
  void CTimer <T>::foo()
  {
  }


>> I'm quite intrigued at this whole C++0x release - I'll have to take a closer look... :)
Look and drool :)
http://en.wikipedia.org/wiki/C%2B%2B0x
evilrixSenior Software Engineer (Avast)

Commented:
Hey Uri, I wrote the following article in your honor :)

http://www.experts-exchange.com/articles/Programming/Languages/CPP/Seperating-C-template-declaration-and-implementation.html

Any feedback on how to improve it would be greatly welcomed (please post directly into the article).

-Rx.

Author

Commented:
Wow! That looks impressive, I and I'm sure many others are very grateful! :)

I'm having to go away for a week on Monday, I'll get this printed off along with a few other articles for some reading and shall be posting feedback when I return. ;) Haha, this is brilliant - it's like building your own C++ book where each topic is written by top professionals! :D

Nice one, cheers! :)
Uni

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial