tags:

views:

222

answers:

5

Hello,

I apologize since this is rather a n00bish question, but I can't figure this one out.

class View //simplified
{
public:
  ROILine* ROI() {return _roi;} //setter does some control stuff...
private:
  ROILine *_roi;
}

class ROI : public eq::Object
{

public:
    //virtual ROI(ROI* copy) = 0;
    virtual ~ROI() {};

    virtual uint32_t getType() = 0;

    virtual void reset() = 0;

    virtual bool addPoint( eq::Vector3f point ) = 0;
    virtual bool previewPoint( eq::Vector3f point ) = 0;

    virtual bool getNotationLocation( eq::Vector3f& point ) = 0;

    virtual bool draw() = 0;

protected:

    enum ROIType {
        NONE = 0,
        LINE,
        POLY,
        AREA,
        VOLUME
    };

    enum ROIMeasure {
        RM_LENGTH = 1,
        RM_AREA,
        RM_VOLUME,
    };

private:

};


class ROILine : virtual public ROI
{

public:
    ROILine();
    ROILine(ROILine* copy);
    ROILine(const ROILine& copy);
    virtual ~ROILine() {SFLog(@"Destroying ROILine: 0x%x",this);};
    void reset();

    float distance() { return _start.distance(_end); }


    // ROI Interface
    uint32_t getType() { return ROI::LINE; }
    virtual bool draw();
    bool addPoint( eq::Vector3f point );
    bool previewPoint( eq::Vector3f point );
    bool getNotationLocation( eq::Vector3f& point );

    eq::net::DataOStream& serialize(eq::net::DataOStream& os) ;
    eq::net::DataIStream& deserialize(eq::net::DataIStream& is) ;

protected:

    enum ROILineState { // RLS_
        RLS_RESET,
        RLS_START,
        RLS_PREVIEW,
        RLS_END,
    };

private:
    uint32_t _state;
    eq::Vector3f _start;
    eq::Vector3f _end;
};

ROILine::ROILine(const ROILine& copy) : ROI()
{
    reset();
    switch (copy._state) 
    {
        case RLS_PREVIEW:
        case RLS_END:
            addPoint(eq::Vector3f(copy._start));
            addPoint(eq::Vector3f(copy._end));
            break;
        case RLS_START:
            addPoint(eq::Vector3f(copy._start));
            break;
        case RLS_RESET:
        default:
            break;
    }
}

/*!
 @abstract resets the line values and state
 */
void ROILine::reset()
{
    _state = RLS_RESET;
    _end = eq::Vector3f::ZERO;
    _start = eq::Vector3f::ZERO;
}
/*!
 @abstract if it has 2 points, draw the line. (_state > _PREVIEW)
 @discussion assumes GL is already set up.  Executes drawing commands.
 @result true if the line was drawn
 */
bool ROILine::draw()
{
    bool retVal = false;

    if (_state >= RLS_PREVIEW) {
        //glTranslatef(0.0f, 0.0f, -1.0f); //Back Up?
        glColor3f( 1.0f, 0.0f, 0.0f );  //Red
        glEnable( GL_LINE_SMOOTH );
        glLineWidth( 1 );
        glBegin( GL_LINES );
        {
            glVertex3fv( _start.array );
            glVertex3fv( _end.array );
        }
        glEnd();    
        //glTranslatef(0.0f, 0.0f, 1.0f); // Return
        retVal =  true;
    } 

    return retVal;

}

// Elsewhere...

View *v = getView(); // returns the view object

// Destroys each time, but works wonderfully
ROILine r = ROILine(*(v->ROI()));
r.draw();

// Does not work (EXC_BAD_ACCESS)
v->ROI()->draw();

// Does not work (EXC_BAD_ACCESS on draw());
ROILine *r = v->ROI();
r->draw(); // debug shows r != 0x0

The Errors I get are as follows when I break on r->draw() and continue.

[Switching to process 12453]
Current language:  auto; currently objective-c++
Warning: the current language does not match this frame.
(gdb) continue 
Program received signal:  “EXC_BAD_ACCESS”.

The "EXC_BAD_ACCESS" occurs on r->draw() or v->ROI()->draw() It doesn't step into the program at all, just halts and bt gives me a ??

My copy constructor works, because the draw() function actually draws where it's supposed to (instead of !!@#$!4!#@ land) What I don't udnerstand, is why copying the value works, but accessing v->ROI()->draw() does not. it MUST have v->ROI() in order to make the copy!!

yes? ... no?

so confused...

Thanks,

A: 

I think ROILine should inherit from ROI. Make draw virtual in ROILine and then

ROILine *r = v->ROI(); r->draw(); // debug shows r != 0x0

should work...

Connected to the problem (or not) It's a bad practice to call a function returning your ROI exactly like ROI constructor.

(referring to: ROILine* ROI() {return _roi;} //setter does some control stuff... )

Maybe the compiler gets confused...

Boaz
If the compiler was confused between `View::ROI()` and `ROI::ROI()` then `v->ROI()` should result in a syntax error and not compile at all (unless it was a really strange and crappy compiler).
TheUndeadFish
+1  A: 
starghost
uhm, I'm pretty sure I said `r != 0x0`
Stephen Furlani
So what is r? And do not tell us it is number 6!
wok
a valid pointer? it looks ok. `r = 0x1f68400`
Stephen Furlani
Oops-- replace "NULL" with the value you get when the pointer is deemed unsafe. Check to see how this value changes when the different blocks are used (non-sequentially).
starghost
A: 

Your copy constructor works as it takes a reference, and to do this you feed it a dereferenced (possibly null) pointer. This is one of those funny things in the language, as a reference to a dereferenced pointer doesn't actually dereference the pointer until you use the reference (C++ really is multi paradigm, it's even lazy!). Since this is undefined behavior, its only by chance that the copy constructor works at all. Its possible that in the View you've invalidated the pointer somehow.

#include <iostream>

using namespace std;

int addOne(int const & n) {
    return n + 1; // Uh oh.
}

int main() {
    int * ptr = 0;
    cout << addOne(*ptr) << endl; // Doesn't crash here.
    return 0;
}
Paul
Just thought that I should clarify: It's not actually lazy. That's just a tongue-in-cheek comment. :)
Paul
+1  A: 

The symptoms make it sound like your ROILine object has been deleted, leaving you with a dangling pointer to freed memory. When you try to call a virtual function, it crashes, because the vtable has been overwritten, but when you create a copy with the copy constructor it gets at least some valid data from the freed object, and appears to work.

Chris Dodd
The instance's vtable pointer is what could be overwritten, not just "the vtable". But otherwise, this is certainly a possibility.
TheUndeadFish
Thanks. The vtable is getting overwritten because my `operator>>` isn't being compiled for some reason - and with no warnings.
Stephen Furlani
+1  A: 

Assuming that everything in your copy constructor for ROILine is working correctly, then here is a possibility: Something has overwritten a few bytes of the ROILine instance returned by View::ROI().

Most likely, the first several bytes of a ROILine object contain a pointer to the virtual function table for that class. (At least this is what typical C++ implementations do.) If those bytes get overwritten then instead of calling through the virtual function table, the program would end up calling through garbage and would almost certainly crash.

However, when making a copy of an object through a copy constructor, that pointer to the virtual function table is probably not accessed at all (unless you were to make a call to a virtual function in the copy constructor). In that case, all the data is copied successfully to a new object that has a correct vtable pointer.

Since draw is vritual, this would explain why calling it on the copy works while calling it on the original does not.

If this is what is happening, then you need to figure out what is overwriting part of your ROILine instance. If that instance is part of another object, then it may be easy. If that instance has been heap allocated individually, then it could be somewhat harder.

TheUndeadFish
Stephen Furlani
@Stephen That sounds a bit odd. A deseralization function shouldn't be touching the vtable pointer.
TheUndeadFish
I confess to not knowing enough about C++ to even make that call. One of the reasons I hate working in this language. Whatever the reason, it works.
Stephen Furlani