Archived

This topic is now archived and is closed to further replies.

Normie

Templated Classes...Oy.

Recommended Posts

Normie    122
I'm trying to make a templated linked list class (just to see if I can). The class I have works fine if I have all of the function bodies in the class, but if I put the bodies outside of the class, I get linker errors out of the wazoo. Here's my class:
  
template<class Data>
struct LISTNODE
{
	Data data;
	LISTNODE<Data>* pPrev;
	LISTNODE<Data>* pNext;
};

template<class Data>
class CLinkedList  
{
public:
	CLinkedList()
	{
		m_pList = NULL;
		m_pFirst = NULL;
		m_pLast = NULL;

		m_numRecords = 0;
	}

	virtual ~CLinkedList()
	{
		LISTNODE<Data>* tempNode = NULL;

		m_pList = m_pFirst;

		do
		{
			if (m_pList)
			{
				tempNode = m_pList->pNext;
				delete m_pList;
			}
			m_pList = tempNode;
		} while (tempNode);
	}

	void AddItem(const Data& data)
	{
		if (!m_pFirst) // if this is the first node

		{
			m_pList = new LISTNODE<Data>; // make the first node

			m_pFirst = m_pLast = m_pList; // set all of the pointers

			m_pList->pNext = m_pList->pPrev = NULL; // Ditto

			m_pList->data = data;
		}
		else
		{
			LISTNODE<Data>* tempNode;

			tempNode = m_pLast;
			m_pLast->pNext = new LISTNODE<Data>;
			m_pLast = m_pLast->pNext;
			m_pLast->data = data;
			m_pLast->pPrev = tempNode;
			m_pLast->pNext = NULL;
			tempNode->pNext = m_pLast;
		}
		m_numRecords++;
	}

	Data operator[](int index)
	{
		LISTNODE<Data>* tempNode;
		int i;

		if (index < 0 || index > m_numRecords - 1)
			return NULL;

		tempNode = m_pList = m_pFirst;

		for (i = 0; i < index; i++)
		{
			tempNode = tempNode->pNext;		
		}

		return tempNode->data;
	}
private:
	LISTNODE<Data>* m_pList;
	LISTNODE<Data>* m_pFirst;
	LISTNODE<Data>* m_pLast;

	int m_numRecords;
};
  
I hate the idea of having all of the code present in the class definition, so if anyone can tell me why pulling the function bodies out of the function results in linker errors, I'd really appreciate it. -Normie --EDIT--Oh, that should be "template above the struct and the class.--/EDIT-- Edited by - Normie on January 29, 2002 11:37:48 AM Grr... the template is "class Data." Why did it remove the brackets???? Edited by - Normie on January 29, 2002 11:38:58 AM Edited by - Normie on January 29, 2002 11:39:59 AM [edit: changed code tags to source tags] Edited by - Magmai Kai Holmlor on January 30, 2002 1:30:05 AM

Share this post


Link to post
Share on other sites
Sandman    2210
This is one of the big headaches I used to have with templates.

A template on its own does not generate any code - the compiler just ignores it. So when you try and use a CLinkedList< int > or whatever, the linker looks for a CLnkedList< int > in a .obj file, and finds nothing. Hence the lovely linker errors.

In order to resolve this, you need to specifically tell the compiler which versions of CLinkedList it needs, so it actually bothers to build them into a .obj file.

To do this, add this line of code into the CLinkedList header file...

              
CLinkedList<int>;
CLinkedList<float>;
CLinkedList<CLinkedList<int>>; // linked list of linked lists!

// etc....



Simple, huh?

To be honest though, I find it easier to leave the implementation in the header file - this works because the cpp that includes the header file has all the information it needs to build the implementation itself - no need to link with a .obj file.


Edited by - Sandman on January 29, 2002 12:52:14 PM

Share this post


Link to post
Share on other sites
Dire.Wolf    122
To defined template class member functions you must write them like so:

template<typename T>
class SomeClass
{
    public:
        SomeClass();
       ~SomeClass();

        T GetSomething();
};

template<typename T>
SomeClass<T>::SomeClass()
{
    // code goes here.
}

template<typename T>
T SomeClass<T>::GetSomething()
{
    // code goes here
}

Now on the other hand you might mean putting the function bodies in a source file (.cpp file) which I'm not sure how to do (if it is even possible.)



Dire Wolf
www.digitalfiends.com

Edited by - Dire.Wolf on January 29, 2002 12:51:31 PM

Share this post


Link to post
Share on other sites
Oluseyi    2103
quote:
Original post by EvilCrap
it would suck ass to do that, sandman. it practically defeats the purpose of user templating.

No offense, but I''ve noticed that you seem to comment on things you don''t understand. What Sandman mentions is called explicit template instantiation (look it up), and is necessary if you wish to export a template class in precompiled (.lib/.dll/.so/.a) form. Which is generally why template classes are distributed as source code.

[ GDNet Start Here | GDNet Search Tool | GDNet FAQ | MS RTFM [MSDN] | SGI STL Docs | Google! ]
Thanks to Kylotan for the idea!

Share this post


Link to post
Share on other sites
Normie    122
ok, so just to make sure I understand...there''s no way to put the source code in a .cpp file and still be able to use it (without specifically declaring the templatized data types)?

*Sigh* That _really_ sucks. :-(

-Normie

Share this post


Link to post
Share on other sites
Dactylos    122
According to the C++ standard you can declare a template with the ''export'' keyword to tell the compiler to export the template-definition for later instantiation (when it needs to be instantiated in another module for example). AFAIK this is currently supported by very few compilers.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
If want your code in the cpp file, you have to include *both* the .h & .cpp files every time OR put an #include "code.cpp" at the bottom of the header.

Alternatively, if you don''t mind having the definitions in the header but want to easily be able to read your class''s functions just do this:

template< typename DATA >
class C
{
public:
// suto functions prototypes:
/*
void Func(void); // all your functions here
*/

// ***************************************
// ***************************************
// actual functions:
void Func(void)
{
}

};

however if you do this you''ll have to remember to always update the suto functions section when you update actual functions'' parameters/return types

Share this post


Link to post
Share on other sites
Normie    122
Dactylos: Does VC++6 support exporting? if it does, can you tell me how to do it in code? Also, will doing this cause any problems later?

Anonymous: Will that method cause any problems later?

-Normie

Share this post


Link to post
Share on other sites
Oluseyi    2103
quote:
Original post by Normie
Dactylos: Does VC++6 support exporting?

No. Neither does 7, IIRC.
quote:
Anonymous: Will that method cause any problems later?

I know he meant "pseudo" rather than "suto" (hey, not everybody is a native English speaker!), but I don''t understand the technique or how it helps...

[ GDNet Start Here | GDNet Search Tool | GDNet FAQ | MS RTFM [MSDN] | SGI STL Docs | Google! ]
Thanks to Kylotan for the idea!

Share this post


Link to post
Share on other sites
Guest Anonymous Poster   
Guest Anonymous Poster
The technique allows all of a class''s functions to grouped together (without code in between & obstructing visibility) for easy reference or whatever. This is a workaround for not being able to both prototype & define a function inside of a class definition without combinging them.

This is to say whereas this is legal:
class A
{
void Func1(void)
{
}
};

Whereas this isn''t (at least in MSVC++):

class B
{
void Func2(void);
void Func2(void)
{
}
};

Share this post


Link to post
Share on other sites
Oluseyi    2103
And what is the use of that? Why would you prototype a function if it''s defined within the class declaration anyway?

It''s like the "template source file" workaround some people have proposed - ".ctt" files - that have to be included in the template header. It''s not beneficial because it means you have to update the code in two places.

Just wait until the export keyword is implemented by most compilers (I expect MSVC8 for Microsoft).

[ GDNet Start Here | GDNet Search Tool | GDNet FAQ | MS RTFM [MSDN] | SGI STL Docs | Google! ]
Thanks to Kylotan for the idea!

Share this post


Link to post
Share on other sites
Houdini    266
quote:
Original post by Oluseyi
And what is the use of that? Why would you prototype a function if it''s defined within the class declaration anyway?



So if you want to get a quick look at what functions a class contains, you can do so easily and quickly without scrolling past function bodies.

quote:
Original post by Oluseyi
It''s like the "template source file" workaround some people have proposed - ".ctt" files - that have to be included in the template header. It''s not beneficial because it means you have to update the code in two places.



Well, I have to update all my normal classes in two places as well. Using .ctt keeps all my classes uniform by keeping the declarations and definitions in seperate files.

Hell, I find it beneficial because I can switch between the declaration and definition of a function easily by just switching between open documents. If they were in the same file I''d need to scroll up and down continiously which would be a pain in the rear.


- Houdini

Share this post


Link to post
Share on other sites
Normie    122
First, thanks to whoever changed my code to source... much clearer, I appreciate it :-)

Second, can someone explain how to use .ctt files (preferably in a way that doesn't make MSVC's ClassView choke)? Or is this something in the docs? (If it is, I haven't found it)

-Normie

Edited by - normie on January 30, 2002 9:57:17 AM

Share this post


Link to post
Share on other sites
Oluseyi    2103
quote:
Original post by Houdini
So if you want to get a quick look at what functions a class contains, you can do so easily and quickly without scrolling past function bodies.

That's what my IDE's there for.

quote:
Original post by Houdini
Well, I have to update all my normal classes in two places as well. Using .ctt keeps all my classes uniform by keeping the declarations and definitions in seperate files.

I hear this argument, but I'll just wait on the export keyword. I don't say you're wrong (for everybody else); I just state my preference.

quote:
Original post by Houdini
Hell, I find it beneficial because I can switch between the declaration and definition of a function easily by just switching between open documents. If they were in the same file I'd need to scroll up and down continiously which would be a pain in the rear.

Again, this is what I have an IDE for (and a very nice one at that).

Normie:
.ctt files are a hack, a workaround until the export keyword becomes well supported/implemented. As such, it is guaranteed to make ClassView choke to a certain extent, but it compiles cleanly. What you do is you place the definitions of your template class methods in a .ctt file and you then include this file in the header.

[ GDNet Start Here | GDNet Search Tool | GDNet FAQ | MS RTFM [MSDN] | SGI STL Docs | Google! ]
Thanks to Kylotan for the idea!


Edited by - Oluseyi on January 30, 2002 6:50:07 PM

Share this post


Link to post
Share on other sites
Houdini    266
You are right, it is pretty much as hack, but it makes inline and templates functions feel so much better to me. As for ClassView, I don''t use it so breaking it doesn''t bother me .

I guess once the export gets implemented we''ll have the best of both worlds .


- Houdini

Share this post


Link to post
Share on other sites
Normie    122
Ok, thanks. :-)

Geez, I thought Microsoft was supposed to be on top of this stuff...ah well, a little older, a little wiser, I guess. :-)

-Normie

Share this post


Link to post
Share on other sites
Shannon Barber    1681
quote:
Original post by Oluseyi
.ctt files are a hack, a workaround until the export keyword becomes well supported/implemented. As such, it is guaranteed to make ClassView choke to a certain extent, but it compiles cleanly. What you do is you place the definitions of your template class methods in a .ctt file and you then include this file in the header.


It''s not as though the ClassView works well with templates to begin with... If you added both the .h and .ctt to your project, it should work just as poorly as with everything in the same file.

Share this post


Link to post
Share on other sites
Normie    122
Sorry, guys, but I gotta bring up another question...

Is there anything I need to remember when inheriting from templated classes? My head hurts too much thinking about it... :-(

-Normie

Share this post


Link to post
Share on other sites
Sandman    2210
I don''t think so...

You can inherit from a template in two ways - you can inherit its ''templatiness'', in which case the inherited class is also a template and subject to the same restrictions, or you can inherit from a specific instantiation of the template, in which case it behaves as a normal class from then on.

  

template < class T >
class blah
{
// blah stuff here

}

// inherit as a general template

template < class T >
class yadda : public blah<T>
{
// yadda stuff here

}

// Inherit from a specific type

class whaffle : public blah<int>
{
// whaffle stuff here

}



Share this post


Link to post
Share on other sites