Sign in to follow this  

Using a template variable within a map

This topic is 4661 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, I want to use a template class and plass the template type (which will be a class) into a map. To help explaing this, this is a section from an include file:
	template<class Type>
	RegisterEntityClass(int iEntityName, class Type);

private:
	static cFactory* mpSingleton;
	std::map<int, Type> InstanceMap; 

Obviously this will not work (or compile) as the map has no knowledge of the template type. I do not want to make the entire class a template. I had thought of dynamically creating the map at runtime within the templated function but am unsure the best way to then store it within the class; for example do i use a void* (urg) or something else? Any help would be greatly appreciated. Thanks, Matt

Share this post


Link to post
Share on other sites
Are you saying you want a singleton Factory class that provides requests for absolutely any type of resource? Assuming this is the case, then here are some suggestions:

Use polymorphism. Create an abstract interface class that all resources implement. Then use a std::map<int, ResourceBase*> (or even use a smart pointer instead of a plain pointer).

Use std::map<int, boost::any>.

Otherwise you'll need to template the class, or use a pre-built factory class, such as that found in the Loki library.

More detail would also help - lack of context means recommending all options - there might be a much better way of doing what you want.

Jim.

Share this post


Link to post
Share on other sites
Thanks for the help. I have also been thinking about this further and am not sure how to acomplish what i want, even overcoming this issue.

Effectivley i want the ability to call a factory class, passing in an identifier, and it returning a pointer to an instance of the required class. This is for two reasons:

1) The instancing class will not need to know the class type.
2) If i change the handling of the objects (e.g. move to handles from pointers) the creation will all be centralised and easier to implelment.

Currently all objects within my code are inherited from an abstract base class for easy use with my scene graph and quad tree (or whatever spatial graph i implement). However, there are obviously derived classes (currrently Object (it has a bound) and entity (it can be selected and manipulated)) Obvioulsy, if my Factory returns the absolute base class i loose the functionality in my higher classes, which is undesirable.

I am currently considering returning an entity from my Object factory, and if i find i need it create more than one object factory of a different base class. However, i will then still need to create the camera and terrain classes seperately as even though these are inherited from the base class they need to implement their own functions.

The ideal situation i was looking for was the ability to register an instance name (e.g. ENTITY) with a partular class and store this in a map. A call to create would then look up the entity class and return the desired pointer. However, although i managed to implement this with template classes and void* pointers (urgh again) I realised i could still not return the required class from the create method and would have to return a base class. Although i could then cast this base pointer back to the deisred one it would mean the create call would have to have knowledge of the actual class, loosing one of the key benefits for me. I really am a little confused as to where to go from here and although i can avoid the issue for now i would using the good old new i would like to get this clear in my mind.

Once again thanks,

Matt

Share this post


Link to post
Share on other sites
Here's my solution to a problem, which may not be a million miles away from what you want. It's basically the Loki object factory implementation. Here's my (stripped-down) code:

First of all, the entitymanager:

#include <map>
#include <vector>
#include <string>

#include <boost/shared_ptr.hpp>
#include <boost/any.hpp>
#include <singleton.h>

class Entity;


class SingletonEntityFactory
{
public:
// I'll comment on this horrendous approach in a mo...
typedef boost::shared_ptr<Entity> (*CreateNewEntityCallback)(const std::vector<boost::any>&);

public:
// This function allows new entity classes to register themselves with the factory
bool RegisterEntityType(std::string whichEntity, CreateNewEntityCallback newEntityFn);
// And they can then be requested using their calling string
boost::shared_ptr<Entity> RequestEntity(const std::string& whichEntity, const std::vector<boost::any>& details);

private:
typedef std::map<std::string, CreateNewEntityCallback> theAvailableEntitiesTypedef_;
theAvailableEntitiesTypedef_ theAvailableEntities_;
};

// Turn this class into a singleton, with a bit of Loki magic!
typedef Loki::SingletonHolder<SingletonEntityFactory> EntityFactory;


// Now some implementation
bool SingletonEntityFactory::RegisterEntityType(std::string whichEntity, CreateNewEntityCallback newEntityFn)
{
return theAvailableEntities_.insert(std::make_pair(whichEntity, newEntityFn)).second;
}

// No error-trapping in here currently - hmm, should really get around to that
boost::shared_ptr<Entity> SingletonEntityFactory::RequestEntity(const std::string& whichEntity, const std::vector<boost::any>& details)
{
theAvailableEntitiesTypedef_::iterator it = theAvailableEntities_.find(whichEntity);
boost::shared_ptr<Entity> thisEntity = (it->second)(details);
return thisEntity;
}




Now the entity interface:

#pragma once

#include <boost/shared_ptr.hpp>

class Entity
{
public:
Entity(void);
virtual ~Entity(void);

virtual void UpdateAI() = 0;
virtual void Render() = 0;
virtual void UpdateLocation() = 0;
};




And now the relevant parts of a specific entity - header excluded, as it's pretty obvious. This is the clever part...

#include stuff

// Create an an anonymous namespace to mangle some names
namespace
{
// Define a creation function
boost::shared_ptr<Entity> thisCallback(const std::vector<boost::any>& details)
{
boost::shared_ptr<Entity> thisEntity(new EntityPlayer(details));
return thisEntity;
};
// This is the clever part - explanation below
const bool registerEntityPlayer = EntityFactory::Instance().RegisterEntityType("Player", thisCallback);
}


EntityPlayer::EntityPlayer(const std::vector<boost::any>& details)
{
// Use the vector<boost::any> to set up some members - again, comments to come on this
}




And finally, how to call:

// Please ignore the magic numbers...
// A callback function for the hotspot, used below
void SensorPathFindCallback()
{
StateManager::Instance().PushState("PathFind");
}

void StateIntroduction::OnEntry
{
// Create a store for the ctor parameters
std::vector<boost::any> thisVec;
// Enter the ctor parameters - this is for a Hotspot - just a clickable text region
int leng = GraphicsManager::Instance().GetStringWidth("Sensor-based Path-finding");
thisVec.push_back(Vector2(400 - leng / 2, 100));
thisVec.push_back(std::string("Sensor-based Path-finding"));
thisVec.push_back(&SensorPathFindCallback);
thisVec.push_back(Vector2(400 - leng / 2, 100));
thisVec.push_back(Vector2(400 + leng / 2, 140));
// Finally, request a pointer to the new entity
// Strictly speaking, my program doesn't work like this, but gives you the idea of usage
boost::shared_ptr<Entity> thisEntity = EntityFactory::Instance().RequestEntity("Hotspot", thisVec);
}




OK - here's how it works.

Each new entity is derived from the Entity base class; in it's cpp file it has that little anonymous namespace piece of code. In the example above, a calling function is defined (which matches in signature to the requirements of entity factory - check the typedefs). This function, and associated calling string, is passed to the factory at program start-up.

This happens because the variable registerEntityPlayer is a global variable (albeit one in it's own little namespace), and hence evaluated at start-up time. In order to calculate this variable, EntityFactory::Instance must be called - creating a static singleton entityFactory - so relying upon the properties of static member variables. All this occurs as the program starts, so as your code starts to execute everything is set up ready to play with - you can start requesting entities from the factory using their calling strings.

Unfortunately, as far as I'm aware it's impossible to have different ctor parameters in your factory-function (ie in the CreateNewEntityCallback typedef) - if anyone can show me how to get around this I'm all ears! I solved this little issue by using std::vector<boost::any>, which to my mind is analogous to C's ... parameter - so, it's goodbye to type-safety. Given that you already have to remember the calling string for each specific entity, I have assumed that remembering the order and type of parameters isn't too much of a pain - if you're particularly worried about it, check on entry to the entity ctor during debug builds. I also assume that there is a small performance hit (if you're worried about that sort of thing) - although this only occurs at the time of object creation.

Note finally that this approach limits compile time dependencies - making adding new entities a breeze. Each entity then handles it's own operations, by using it's public interface (through the Entity base class) as it's only calling parameter external to the object itself.

Quote:

However, although i managed to implement this with template classes and void* pointers (urgh again) I realised i could still not return the required class from the create method and would have to return a base class. Although i could then cast this base pointer back to the deisred one it would mean the create call would have to have knowledge of the actual class, loosing one of the key benefits for me.


I think you can do this using a Clone method, but off the top of my head I can't remember how to do it. Will have a look around.


So - having now exposed the inner-secrets of my programming style, anyone passing can feel free to rip it to pieces. It would be only polite[smile].

Jim.

Edit : on the clone method : you might want to have a look at snk_kids last example in this thread. Although I think it will increase compile time dependencies to integrate this method (because you'll have to increase the number of #includes). Horses for courses though - depends on what's important to you.

[Edited by - JimPrice on March 8, 2005 6:06:06 PM]

Share this post


Link to post
Share on other sites
Fantastic. I will have to look through that tomorrow to make sure i understand it (too late/early now, not a chance of getting my head around it!) but thanks for taking the time to provide it.

Matt

Share this post


Link to post
Share on other sites

This topic is 4661 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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