Splitting a template and class into definition and declaration.

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

LVL 3
Unimatrix_001Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

evilrixSenior Software Engineer (Avast)Commented:
You again? :-p
Unimatrix_001Author Commented:
Hehehe, afraid so. ;) Still... at least these are simpler and less awkward. :)
evilrixSenior 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

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Amazon Web Services

Are you thinking about creating an Amazon Web Services account for your business? Not sure where to start? In this course you’ll get an overview of the history of AWS and take a tour of their user interface.

Unimatrix_001Author 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/
Unimatrix_001Author 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! ;)
jkrCommented:
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

Unimatrix_001Author Commented:
Oh! It works! :)
Unimatrix_001Author 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.
Unimatrix_001Author 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).
Unimatrix_001Author 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

Unimatrix_001Author 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

Unimatrix_001Author 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 :)
Unimatrix_001Author 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.
Unimatrix_001Author 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
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C++

From novice to tech pro — start learning today.