Probably overly complicated template class not working

Started by
2 comments, last by KanonBaum 12 years, 10 months ago
[heading]Scenario:[/heading]
I have an object class that needs to access component-like pieces from a "manager" class. (To shrink getSystem()->getInput()->getKey() etc... Basically rid of lengthy tags. )
Unfortunately, the class is not aware of the manager and cannot access it's components directly. SO, to wrap around this, I've designed this code:

[heading]Object Code snippet:[/heading]
Object.h

// Forward declare the manager
class Manager; // Stores components and other various materials

// Derive DeviceImpl later for real usage.
// This is just as a temp placement since
// Manager is currently inaccessible.
class DeviceImpl
{
private:

public:
DeviceImpl() { ; }
virtual ~DeviceImpl() { ; }

template <typename Device>
Device *getDevice()
{
std::cout << "DeviceImpl returning null in getDevice" << std::endl;
return 0;
}
};

class Object
{
private:

friend Manager;

DeviceImpl *device_impl;

void setDevice(DeviceImpl *device)
{
std::cout << "Object: adding device" << std::endl;
device_impl = device;
}

public:

template<typename Device> Device *getDevice()
{ return device_impl->getDevice<Device>();}

Object() { device_impl = 0; }
~Object() { ; }
};


[heading]Manager code snippets[/heading]
Manager.h

#include "Object.h" // Manager stores series of objects and updates them

class DeviceHandler;

class Manager
{
private:
Graphics *graphics;
Input *input;
Factory *factory;
Camera *camera;
Map *map;

DeviceImpl *device;

public:

Graphics *getGraphics();
Input *getInput();
Factory *getFactory();
Camera *getCamera();
Map *getMap();

void Manager::addObject(Object *newObject)
{
// If null, stop function
if (!newObject)
return;

// Clamp entity data into a shared pointer
boost::shared_ptr<Object> temp(newObject);

// Add the manager
temp->setDevice(device);

// Initialize
temp->init();

// Push back onto the list
vObjects.push_back(temp);
}

Manager();
~Manager();
};

/*
Now define the DeviceHandler because manager is defined
*/

class DeviceHandler : public DeviceImpl
{
private:
Manager *manager;

public:

DeviceHandler(Manager *m) : DeviceImpl()
{ manager = m; std::cout << "Manager set" << std::endl; }

template <typename Device>
Device *getDevice()
{
std::cout << "Getting a device..." << std::endl;

if(!manager)
{
std::cout << "returning null" << std::endl;
return 0;
}

Device* deviceTypeTest = 0;

if( (deviceTypeTest = dynamic_cast<Graphics*>(deviceTypeTest) != 0) )
{
std::cout << "returning graphics" << std::endl;
return manager->getGraphics();
}
else if( (deviceTypeTest = dynamic_cast<Input*>(deviceTypeTest) != 0) )
{
std::cout << "returning input" << std::endl;
return manager->getInput();
}
else if( (deviceTypeTest = dynamic_cast<Camera*>(deviceTypeTest) != 0) )
{
std::cout << "returning camera" << std::endl;
return manager->getCamera();
}
else if( (deviceTypeTest = dynamic_cast<Map*>(deviceTypeTest) != 0) )
{
std::cout << "returning Map" << std::endl;
return manager->getMap();
}
else if( (deviceTypeTest = dynamic_cast<Factory*>(deviceTypeTest) != 0) )
{
std::cout << "returning factory" << std::endl;
return manager->getFactory();
}
else if( (deviceTypeTest = dynamic_cast<Manager*>(deviceTypeTest) != 0) )
{
std::cout << "returning manager" << std::endl;
return manager;
}

std::cout << "returning null" << std::endl;

return 0;
}
};


Manager.cpp

// ... Lots of other important stuff but finally define the constructor and deconstructor

//Ctors and dtors
Manager::Manager()
{
device = new DeviceHandler(this);

// Lots mo stuff
}

Manager::~Manager() { ; }


[heading]Example of usage[/heading]

class Ball : public Object
{
private:
Sprite ball;
// ... Basic initialize stuff

public:
// Draw routine
void draw()
{
std::cout << "Ball: Drawing. Getting graphics." << std::endl;
getDevice<Graphics>()->draw(ball, getX(), getY());
{

// Some Mo' stuff...
};


[heading]Problem[/heading]
Now this all compiles fine and runs! But it does not seem to ever seem to refer to getDevice() in DeviceHandler. Instead, it still seems to use DeviceImpl.

[heading]Output:[/heading]
Manager set
Object: adding device
Ball : Drawing. Getting graphics.
DeviceImpl returning null in getDevice.

[heading]This is where you help[/heading]
Perhaps it's a logic error that I'm missing or even a better design would be helpful. So to you future people: thanks!
I'm that imaginary number in the parabola of life.
Advertisement
Seems Object stores a DeviceImpl pointer, and since get_device() isn't virtual, you will be using DeviceImpl::get_device(). Perhaps you want it to be virtual??
I can't be of much help here, but I think your problem is that the templated function is not virtual so you are always getting DeviceImpl::get_device() from a DeviceImpl pointer. Unfortunately I don't think you can tag a templated function as virtual since its not really a function until its instantiated somehow.
This isn't a nice design. For one thing dynamic_casts are costly, and you certainly wouldn't want to use them every time you call any function in your entire game. Don't add runtime complexity just to get out of having to type some extra text! Also don't add complex design to get out of it either. If you want your Object class to be able to access these different systems either put them into a globally accessible object, or pass them to the Object in the constructor. But my opinion is that it is better to separate your operations from your data:
Object contains the specification for an Object (i.e. the data: position, colour etc.)
Another class (e.g. Renderer) contains the methods to draw an Object.
Another class (e.g. Scene) contains the set of object instances you wish to draw.
So you create a bunch of Objects, add them to a Scene, and then pass the Scene to the Renderer to actually do the drawing.
You're so right. I just thought it would be all fancy and cool which is NOT a good programming practice.
Oh well. I learned. Thanks! :D
I'm that imaginary number in the parabola of life.

This topic is closed to new replies.

Advertisement