Manually implementing template functions

Started by
8 comments, last by Dragon_Strike 15 years, 11 months ago
Hello, I have a bit of a tricky question about templates. I have a class representing a level in an editor something like as follows: EditorLevel.h

class EditorLevel
{
    public:

	// Get a list of objects from the level:

	template<typename T> list<T*> & get();

    protected:

	// Level objects

        list<GuiPoint*>	points;
        list<EditorLineStrip*> strips;
        list<EditorObject*> objects;
        list<EditorLevelForce*>	forces;
        list<EditorMoverObject*> movers;
        list<EditorLevelTrigger*> triggers;
        list<EditorLevelSound*> sounds;
        list<EditorFXEmitter*> emitters;
};

I have lists of various objects stored inside the level and I have declared a template function called 'get' which I want to use for accessing the various lists of objects. I'm trying to make one nice clean interface for accessing all these types of lists. For example if get<GuiPoint>() is called then the points list should be returned, and if get<EditorLineStrip>() is called then the strips list should be returned in that case and so forth.. So I'll need some way of manually implementing all the template versions myself. I'm just wondering is this possible at all to do, or does it require compiler specific constructs ? I'm using VC++ 8. I've tried something like this but it doesn't work: EditorLevel.cpp


// .. 

list<GuiPoint*> & EditorLevel::get<GuiPoint>(){ return points; }
list<GuiPoint*> & EditorLevel::get<EditorLineStrip>(){ return strips; }

// and so on ..

The compiler returns the following error for each of these functions: error C2768 : illegal use of explicit template arguments Thanks, Darragh
Advertisement
You can use template specialisation:
template<>list<GuiPoint*> & EditorLevel::get<GuiPoint>(){ return points; }template<>list<EditorLineStrip*> & EditorLevel::get<EditorLineStrip>(){ return strips; }


However, returning member data by non-const reference is often a sign of poor design. Privacy adds nothing if you hand out references like that. Either make the data public, or implement the functionality inside the class.

[edit: terminology]
Its called "template specialization" and it works precisely how you would think it would. Depending on the interface you're going for, you might be wanting member function templates, rather than class templates, but the specialization stuff still applies.

throw table_exception("(? ???)? ? ???");

Quote:Original post by rip-off
You can use template specialisation:
*** Source Snippet Removed ***

However, returning member data by non-const reference is often a sign of poor design. Privacy adds nothing if you hand out references like that. Either make the data public, or implement the functionality inside the class.

[edit: terminology]


Yeah it's not good design, you're right there; however it will do for the time being. At the moment I'm refactoring an existing code-base (not my own mind) and trying to improve upon it before adding new features to the application. It's a delicate operation and I need to proceed one step at a time; this EditorLevel class is the latest step- previously the level data for the editor was stored in a Gui Widget !! Ugh..

Anyhows, I tried the code out and it came back with the following error:

error C2908: explicit specialization; 'std::list<_Ty> &EditorLevel::get<GuiPoint>(void)' has already been instantiated
with
[
_Ty=GuiPoint *
]


Going by the error I tried the following:

template<> list<> & EditorLevel::get<GuiPoint>(){ return points; }//.. rest of the functions


It works! Thanks for that guys. Just goes to show though how tricky some of the finer points of C++ can be; even after years of working with the language you still end up learning new things about obscure situations like this. [smile]
Works for me TM (in both GCC and MSVC)

class GuiPoint {};class EditorLineStrip {};class EditorLevel{    public:	// Get a list of objects from the level:	template<typename T> std::list<T*> & get();    protected:	// Level objects        std::list<GuiPoint*>	points;        std::list<EditorLineStrip*> strips;};template<>std::list<GuiPoint*> & EditorLevel::get<GuiPoint>(){ return points; }template<>std::list<EditorLineStrip*> & EditorLevel::get<EditorLineStrip>(){ return strips; }int main(){    EditorLevel level;}


Nice to see some other Irish people around [smile]
Quote:Original post by rip-off
Works for me TM (in both GCC and MSVC)

*** Source Snippet Removed ***

Nice to see some other Irish people around [smile]


[smile]

That code you posted works fine for me too. Wait till you see this though- it's utterly weird:

This is the full header file (so far) for the object:

EditorLevel.h

#ifndef EDITOR_LEVEL_H#define EDITOR_LEVEL_H#include <list>class GuiPoint;class EditorLineStrip;class EditorObject;class EditorLevelForce;class EditorMoverObject;class EditorLevelTrigger;class EditorLevelSound;class EditorFXEmitter;using std::list;class EditorLevel{    public:	// Destructor:	~EditorLevel();	// Clears the level:	void clear();	// Get a list of objects from the level:	template<typename T> list<T*> & get();    protected:	// Clears a list of level entities	template<typename T> void clearList( list<T*> & l )	{		list<T*>::iterator i = l.begin();	        list<T*>::iterator e = l.end();	        while ( i != e ){ delete * i; i++; }	}	// Level objects        list<GuiPoint*>	points;        list<EditorLineStrip*> strips;        list<EditorObject*> objects;        list<EditorLevelForce*>	forces;        list<EditorMoverObject*> movers;        list<EditorLevelTrigger*> triggers;        list<EditorLevelSound*> sounds;        list<EditorFXEmitter*> emitters;};#endif


And here is the source code:

EditorLevel.cpp

#include "EditorLevel.h"#include "GuiPoint.h"// DestructorEditorLevel::~EditorLevel(){ clear(); }// Clear the levelvoid EditorLevel::clear(){        // Clear all object lists	clearList<GuiPoint>(points);	clearList<EditorLineStrip>(strips);	clearList<EditorObject>(objects);	clearList<EditorLevelForce>(forces);	clearList<EditorMoverObject>(movers);	clearList<EditorLevelTrigger>(triggers);	clearList<EditorLevelSound>(sounds);	clearList<EditorFXEmitter>(emitters);        // ** TEST: MAKE SURE TEMPLATE SYNTAX IS OK	list<GuiPoint*> l = get<GuiPoint>();}// Get a list of objects from the level:template<> list<GuiPoint*> & EditorLevel::get<GuiPoint>(){ return points; }


The code in the source file above produces the C2908 error complaining about the template already being initialized. However.. If I remove this test statement:

// ** TEST: MAKE SURE TEMPLATE SYNTAX IS OKlist<GuiPoint*> l = get<GuiPoint>();


.. Then the code compiles fine. The code also compiles if I move the explicitly instantiated version of get<>() above where this statement is in the source file.

So it seems that the test statement is initializing the get<GuiPoint>() version of the template function already, even though I only had a declaration of the template function
in the header file and not an implementation.

This is very odd.. A bug in the compiler perhaps ? Or is it standard C++ behavior ? Weird.. Whatever it is.
Instead of handing out list references, try having a set of template functions which lookup the objects themselves; for example:

class Catalogue{public:	template <class T, class I>	T* GetObject (I index) {		std::list<T>* lst = GetList<T> ();		if (lst) {			std::list<T>::iterator i = lst->begin();			while (index--) i++;			return &(*i);		} return 0;	} private:	// lists	std::list <ClassOne> _one;	std::list <ClassTwo> _two;	std::list <ClassThree> _three;	std::list <ClassFour> _four;	std::list <ClassFive> _five; 	// list accessors	template <class T> std::list <T>* GetList () { return 0; }	template <> std::list <ClassOne>* GetList <ClassOne> () { return &_one; }	template <> std::list <ClassOne>* GetList <ClassTwo> () { return &_two; }	template <> std::list <ClassOne>* GetList <ClassThree> () { return &_three; }	template <> std::list <ClassOne>* GetList <ClassFour> () { return &_four; }	template <> std::list <ClassOne>* GetList <ClassFive> () { return &_five; }};
Quote:Original post by thre3dee
Instead of handing out list references, try having a set of template functions which lookup the objects themselves; for example...


That is a terrible idea. First of all, std::lists offer O(n) lookups by index, so are precisely the wrong datatype for such an interface. In any case, it doesn't really address the underlying design issue (you've traded handing list references for individual object references).

@Darragh

I have no idea. I can't say I've encountered many situations where I've needed to specialise a template.

GCC has a similar error message:
Quote:
specialization of std::list<T*, std::allocator<T*> >& EditorLevel::get() [with T = EditorLineStrip] after instantiation
Quote:Original post by rip-off
@Darragh

I have no idea. I can't say I've encountered many situations where I've needed to specialise a template.


Yeah it's an odd one alright. This is the first time I've encountered this myself; I don't even use templates that much either..

Quote:Original post by rip-off
GCC has a similar error message:
Quote:
specialization of std::list<T*, std::allocator<T*> >& EditorLevel::get() [with T = EditorLineStrip] after instantiation


Hmmm.. It must be a standard C++ thing then if the GCC compiler has this problem also. It mystifies me how it could instantiate the function when no implementation had been provided up until that point. Maybe it just creates a default 'blank' implementation automatically.. That wouldn't exactly be good news if that is the case; god only knows what the reference could point to after calling that function! I'll have to watch out for this in future when working with specialized templates [smile]
just look up "metatemplate programming", typelists and the boost mpl library.. it'll help u out...


[Edited by - Dragon_Strike on May 14, 2008 4:55:30 PM]

This topic is closed to new replies.

Advertisement