Casting From shared_ptr<Base> to shared_ptr<Derived>

Started by
5 comments, last by the_edd 12 years, 3 months ago
So I have a manager that stores entities with a std::vector< std::shared_ptr<Entity> >. Here is a relevant outline of the manager:


#define SharedPtr std::tr1::shared_ptr

class EntityManager
{
private:
std::vector<SharedPtr<Entity> > mEntities;
std::vector<SharedPtr<Entity> > mTemplateEntities;


public:
CreateEntity(std::string templateName, hx::Vector2D pos, std::string handle);
SharedPtr<Entity> GetEntity(int id);
SharedPtr<Entity> GetEntity(std::string handle);
SharedPtr<Entity> GetTemplateEntity(std::string name);
};



Entity is an abstract base class, with a derived class Actor. I add entities to the manager by doing the following:


void EntityManager::CreateEntity(std::string templateName, hx::Vector2D pos, std::string handle)
{
SharedPtr<Entity> entity( mWorld->GetEntityManager()->GetTemplateEntity(templateName) );
entity->SetPos(pos);
entity->SetHandle(handle);
if (entity->GetType() == ACTOR)
{
SharedPtr<Actor> actor( new Actor() );
*actor.get() = *(Actor*)entity.get();
mWorld->GetEntityManager()->AddEntity( actor );
}
}



To access an entity in the manager, I call EntityManager::GetEntity(62). If the entity accessed needs to be used as an Actor (derived type), I will use this:


std::dynamic_pointer_cast<Actor>(manager->GetEntity(2))


However, I am facing a problem with implementing a player system. This is an outline of the player class:


class Player
{
private:
SharedPtr<Actor> mTarget;
public:
bool Player::Init(std::string handle, EntityManager* manager)
{
//the following is just a test, and it works
SharedPtr<Actor> test(std::dynamic_pointer_cast<Actor>(manager->GetEntity(handle)));

//this next line (what I actually want to do), gives an unhandled exception)
mTarget = std::dynamic_pointer_cast<Actor>(manager->GetEntity(handle));

//I have also tried to do the following, but it still crashes
mTarget = test;

return mTarget;
}
};



When I run it in the Visual C++ 2010 debugger, I get an unhandled exception, in the middle of the swap() method in the utility standard header (line 103) whenever I try to assign a SharedPtr<Actor> to mTarget. Is there a more correct way to cast from the base class Entity to the derived class Actor? Do I need to initialize mTarget somehow?
Advertisement
This line looks scary:


[color=#666600]*[color=#000000]actor[color=#666600].[color=#000088]get[color=#666600]()[color=#000000] [color=#666600]=[color=#000000] [color=#666600]*([color=#660066]Actor[color=#666600]*)[color=#000000]entity[color=#666600].[color=#000088]get[color=#666600]();


If you're going down the route of performing dynamic casts, you'd need one here too, presumably?

Can you strip down the code in order to provide a small single-source-file example that illustrates the problem?
Maybe a little better type safety next time? I believe it would be accomplished by calling shared_ptr::reset on actor to assign it to a new pointer, and then static_cast to down cast from based to derived.

actor.reset(static_cast<Actor*>(entity.get()))

This line looks scary:


[color=#666600]*[color=#000000]actor[color=#666600].[color=#000088]get[color=#666600]()[color=#000000] [color=#666600]=[color=#000000] [color=#666600]*([color=#660066]Actor[color=#666600]*)[color=#000000]entity[color=#666600].[color=#000088]get[color=#666600]();


If you're going down the route of performing dynamic casts, you'd need one here too, presumably?

Can you strip down the code in order to provide a small single-source-file example that illustrates the problem?


Well, I discovered that I was actually putting the player in a shared_ptr elsewhere in my code, so instead of declaring a Player* player = new Player(); I was doing shared_ptr<player>;

Anyways, it works now, but about the [color=#666600]*[color=#000000]actor[color=#666600].[color=#000088]get[color=#666600]()[color=#000000] [color=#666600]=[color=#000000] [color=#666600]*([color=#660066]Actor[color=#666600]*)[color=#000000]entity[color=#666600].[color=#000088]get[color=#666600]() I am only trying to copy the data pointed by the shared pointer for the template entity. Should I implement some copy method instead? I just want the actor to have all of the data contained in the entity. However, the entity shared_ptr<> may actually have some actor specific data, so that is why I cast it to an actor. Maybe this is a better alternative? *actor.get() = *std::dynamic_pointer_cast<actor>(entity).get();
If you're not going to check for NULL, then you might as well use static_pointer_cast.
Why not mWorld->GetEntityManager()->AddEntity( entity->GetCopy() );
? -- or something like that.

Meaning if the member function you ultimately want to call is "AddEntity" and you have an entity, seems weird to have to cast to a specific type first.

[...][color=#666600] I am only trying to copy the data pointed by the shared pointer for the template entity. Should I implement some copy method instead?


That piece of code is saying "factory" to me quite loudly.


// somewhere
theEntityManager.AddEntityCreator("Player", shared_ptr<EntityCreator>(new PlayerCreator));
theEntityManager.AddEntityCreator("Goblin", shared_ptr<EntityCreator>(new GoblinCreator));
theEntityManager.AddEntityCreator("Squirrel", shared_ptr<EntityCreator>(new SquirrelCreator));
theEntityManager.AddEntityCreator("MilkSnatcher", shared_ptr<EntityCreator>(new MargaretThatcherCreator));

// ...
void EntityManager::CreateEntity(const std::string &templateName, hx::Vector2D pos, const std::string &handle)
{
shared_ptr<EntityCreator> creator = LookupCreator(templateName); // using std::map internally, perhaps
if (creator)
AddEntity(creator->Create(pos, handle));
else
throw doh(); // assert or return false, to taste
}


I don't much care for "manager" classes either, but that's another story smile.png

This topic is closed to new replies.

Advertisement