Sign in to follow this  
Darragh

Manually implementing template functions

Recommended Posts

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

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites
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"

// Destructor

EditorLevel::~EditorLevel(){ clear(); }

// Clear the level

void 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 OK

list<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.

Share this post


Link to post
Share on other sites
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; }
};

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this