Jump to content
  • Advertisement
Sign in to follow this  
thedustbustr

Nasty OO design issues

This topic is 5065 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

My code crashes when I cast "sideways" (from one derived class to another derived class of a common base class). I'm using C++ with VC71. A TangibleObject has orientation and position. A VisibleObject:public TangibleObject has a draw() method to provide its own mechanism to draw itself. A Controller:public TangibleObject adds pure virtual methods to TangibleObject to move or rotate the object, facilitating human control by abstracting low level rotations through yaw,pitch,roll,move methods. NaturalController:public Controller implements these methods to provide natural quake-style first person controls. Controller and its derived classes add only methods, not variables. Camera:public VisibleObject is just a visible representation of a camera. A typical scene might have three cameras, and each camera is visible to the others. For our purposes they might as well be cubes or trees or people. This has NOTHING to do with the math/OGL camera (this is taken care of by TangibleObject and invert pushing it to OGL to see from the object's viewpoint).
    TangibleObject* camera=new Camera; //create a new object
    Controller control=(NaturalController*)camera; //take control of the object
    control->move(vector3(-.5,-.5,0)); //move it

The third line crashes. I have previously been able to cast "sideways" like this to great effect. Perhaps I have been lucky: I feel like I am playing with fire, pushing OO to my compiler's limit. Is this allowed? Why is it crashing? My previous success was with simpler inheritance- perhaps the polymorphic nature of Controller is getting in the way? Relevant headers (should be sufficiently concise)
class Object
{public:
    unsigned int id;
    std::string name;
    Object(std::string str="");
    virtual ~Object();
};

//has an orientation and position in the world
class TangibleObject : public Object
{
protected:     
    matrix44 _world;

public:
    matrix44 world(); //accessor for _world

    void scale_local     (const double magnitude);
    void rotate_local    (const vector3& axis, const double radians);
    void translate_local (const vector3& v);
    void scale_world     (const double magnitude);
    void rotate_world    (const vector3& axis, const double radians);
    void translate_world (const vector3& v);

    vector3 right();
    vector3 up();
    vector3 forward();

    vector3 global_right();
    vector3 global_up();
    vector3 global_forward();

    TangibleObject();

    void dump(int x, int y); //debugging (x,y)=screen pixels
};

class Controller : public TangibleObject
{
public:
    virtual void pitch(double rad)=0;
    virtual void yaw(double rad)=0;
    virtual void roll(double rad)=0;
    virtual void move(vector3 d)=0;
};

class NaturalController : public Controller
{
public:
    void pitch(double rad);
    void yaw(double rad);
    void roll(double rad);
    void move(vector3 d);
};

//visible
class VisibleObject : public TangibleObject
{ 
public: 
    virtual void draw()=0;
    VisibleObject();
    ~VisibleObject(){}
};


class Camera : public VisibleObject
{
public:
    void draw();
};

Share this post


Link to post
Share on other sites
Advertisement
Why did you expect this to work? You're calling a virtual function from Controller in a class that doesn't have Controller in it's inheritance hierarchy. Which means that the vtable entries won't be in the object. So trying to call it as if the Controller vptr was there means you're calling into what's more or less random memory. Severe bad mojo.

If you want Camera to support operations from the Controller class, then derive it from Controller.

Share this post


Link to post
Share on other sites
[edit] Crash resolved (incomplete update to new code). Works as expected :)

If I change NaturalController so that it inherits directly from TangibleObject, short circuiting the abstract base class Controller, and change the code accordingly, I get:
Unhandled exception at 0x00000000 in katana4.exe: 0xC0000005: Access violation reading location 0x00000000.

00000000 ???
00000001 ???
00000002 ???
00000003 ???
00000004 ???





and it dumps me to a useless debugger dissassembly shown above. I'm fairly certain I'm not trying to dereference an uninitialized pointer (which would explain the 0x00000000 in debug mode and the crash). WTF

[edit]
@SiCrane, OK, here there aren't virtual functions, so what you described is not happening. All I want to do is use Controller methods on a TangibleObject's data, which should be kosher because a Controller IS a TangibleObject

[Edited by - thedustbustr on January 2, 2005 1:48:48 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by thedustbustr
All I want to do is use Controller methods on a TangibleObject's data, which should be kosher because a Controller IS a TangibleObject
Nope. You can't guarantee that a TangibleObject is a Controller. Time to refactor. Consider free-standing functions.

Share this post


Link to post
Share on other sites
It was just luck. If I had to guess, I would say that there was a pointer patchup that didn't work properly as a result of casting which sent program control spinning into la la land.

As I said before, if you want to use Controller's member functions with the Camera class, than derive from Controller. Or as Oluseyi suggests, refactor to use free standing functions.

As a general rule: if you do something with a C style cast that a static_cast<> or a dynamic_cast<> wouldn't allow, then you're probably stepping into the land of undefined behavior.

As another rule of thumb: casts in general are a bad sign.

Share this post


Link to post
Share on other sites
@Oluseyi, Why not? And I know that a TangibleObject is not necessarily a Controller, but a Controller is always a TangibleObject.

[edit] It works, BTW. Just resolved the crash. ...or am I lucky again
[edit] Err, maybe not, I've updated the code several times since this... I'll have to look into it some more

Share this post


Link to post
Share on other sites
Casting 'sideways', as you put it, simply doesn't work. In your instance, a Camera is NOT a Controller (in other words, Controller is not in Cameras hierarchy, either up or down). You can ONLY cast up the hierarchy (which always works), or down (only if you KNOW for certain that the object is of that type). As SiCrane, the simple solution is to derive Camera from Controller (or NaturalController).

Though at this point you're starting to run into a class-explosion problem. Not everything is best described through inheritance: composition is also your friend.

For instance: Controller's job is really just to manipulate the position and rotation of a TangibleObject. Since these are publically accessible qualities, it's possible to remove Controller from the hierarchy entirely and simply have it compose a TangibleObject, as such:


class Controller
{
protected:
TangibleObject* _object;
public:
Controller(TangibleObject* object):_object(object) {}

virtual void pitch(double rad)=0;
virtual void yaw(double rad)=0;
virtual void roll(double rad)=0;
virtual void move(vector3 d)=0;
};



Now your concrete controller (eg. NaturalController) implements pitch,yaw,roll, and move by manipulating _objects position and rotation. Then controlling your camera becomes:


Controller* control=new NaturalController(camera);
control->move(vector3(-.5,-.5,0));



In fact, most problems can be solved using composition instead of inheritance, and it usually ends up much cleaner.

Morbo

Share this post


Link to post
Share on other sites
Quote:
Original post by thedustbustr
@Oluseyi, Why not? And I know that a TangibleObject is not necessarily a Controller, but a Controller is always a TangibleObject.
What bearing does that have on anything? You're trying to cast a Camera into a Controller (invalid), and then invoke Controller methods on the TangibleObject portion of the object. You might want to look up "slicing."

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!