undefined reference to `vtable for Segment' due to virtual destructor???

D

druberego

I read google and tried to find the solution myself. YES I do know that
you can get undefined references if you:
a) forget to implement the code for a prototype/header file item, or
b) you forget to pass all the necessary object files to the linker.
Neither of those are my problem. Please bear with me as the question
I ask is rather long and I think it's beyond a CS101 level of linker
stupidity. If it is a stupid CS101 mistake I'm making I'll be quite
surprised.

Some other items: I am compiling with -Wall and NO warnings are
given during the compilation stages.

I've known how to code in C++ since 1991 but this is the first project
where I ever used a pure abstract base class.

During linking I'm getting this: (Which by the way I wasn't getting a
week ago and I haven't changed the destructors in any of my files.)

jeffw@mail:Automatic$ make test
g++ -g -O3 -Wall -pipe -fpic -I/usr/share/qt4/mkspecs/linux-g++
-I/usr/include/qt4/QtCore -I/usr/include/qt4/QtGui -I/usr/include/qt4
-o test test.o libgeometry.a -lm -L/usr/lib64/qt4 -lQtGui -L/usr/lib64
-L/usr/lib64/mysql -L/usr/lib64/qt4
test.o:(.data.rel.ro+0x0): undefined reference to `vtable for Segment'
libgeometry.a(Line.o): In function `~Segment':
/home/jeffw/Automatic/Segment.hpp:43: undefined reference to `vtable
for Segment'
/home/jeffw/Automatic/Segment.hpp:43: undefined reference to `vtable
for Segment'
libgeometry.a(Line.o):(.data.rel.ro._ZTI4Line[typeinfo for Line]+0x10):
undefined reference to `typeinfo for Segment'
libgeometry.a(Edge.o): In function `~Segment':
/home/jeffw/Automatic/Segment.hpp:43: undefined reference to `vtable
for Segment'
/home/jeffw/Automatic/Segment.hpp:43: undefined reference to `vtable
for Segment'
collect2: ld returned 1 exit status

Here's the command that created libgeometry.a:
ar rcs libgeometry.a Entity.o GeometricEntity.o Point.o Vector.o
Segment.o Line.o Ray.o Edge.o Circle.o Arc.o Intersection.o
CNCProfile.o GlobalParameters.o

So yes, I do think I included Segment.o, Line.o and Entity.o which
shouldn't really matter since almost all of these include
Segment.hpp where the destructor body is actually defined.

What the heck does that mean??? Yes, I know it looks like I was
boob who forgot to implement a code body for a destructor. But
here's a couple of ideas.

The descended classes only contain primitive members or
members who's destructor can be the default destructor.
Since the class is provided with a default destructor doesn't that
mean I don't have to supply since all I would be doing is rewriting
the default anyways?

Second here's the slightly edited code for Segment.hpp
class Segment : public GeometricEntity
{
public:

static const unsigned char CONTINUES_BEFORE = 0x1;
static const unsigned char CONTINUES_AFTER = 0x2;

virtual ~Segment(void) {};

virtual Segment *clone(void) const = 0;
virtual Segment *clone(double beg, double end) const = 0;

virtual bool includes(const Point &p) const = 0;
virtual bool includes(double s) const = 0;
virtual bool isendpoint(double s) const = 0;

// functions for obtaining a point relative to the segment
//
virtual double scale(const Point &p) const = 0;

// Return a point relative to the endpoints.
// 0.0 = near end point
// 1.0 = the far end point as given to the constructor
virtual Point point(double scale) const = 0;

/* returns a list of scales relative to this, that identif
y the point
of intersection with the other segment */
virtual list<Intersection> intersect(const Segment *segmen
t) const = 0;

/* returns a list of scales relative to the other line, th
at identify
the points of intersection with this segment */
virtual list<Intersection> intersectwith(const Line &line)
const = 0;
/* returns a list of scales relative to the other Circle,
that identify
the points of intersection with this segment */
virtual list<Intersection> intersectwith(const Circle &cir
cle) const = 0;

virtual unsigned char relation(double s) const;

virtual std::eek:stream &print(std::eek:stream &o) const = 0;

virtual void draw(QPainter &qp, double diameter = 0.0) con
st = 0;

virtual std::eek:stream &GCodeDepth(std::eek:stream &o, double d
epth) const = 0;

friend std::eek:stream &operator<<(std::eek:stream &o, const Seg
ment *s);
}

Code for Segment.cpp
#include "Segment.hpp"

ostream &operator<<(ostream &o, const Segment *s)
{
return s->print(o);
}

Here's the code for Line.hpp
class Line : public Segment
{

protected:
Point p; // line parameters
Vector d; // direction of line, no other meaning

public:
Line(const Point &point, double radians)
: p(point), d(Vector(radians)) {};
Line(const Point &point, const Vector &direction)
: p(point), d(direction) {};

virtual Line *clone(void) const { return new Line(*this);
}
virtual Line *clone(double beg, double end) const;

virtual bool includes(const Point &point) const;
virtual bool includes(double) const { return true; }
virtual bool isendpoint(double) const { return false; }

// assuming that pnt lies on the line colinear with the se
gment,
// returns the value that would result in pnt = scale(valu
e) being
// true.
virtual double scale(const Point &pnt) const;

// return a point along the line colinear with this line s
egment.
// If 0<scale<1 then the point lines on the segment itself
..
// the vector D is scaled to obtain the point on the line.
virtual Point point(double scale) const;

/* returns a list of scales relative to this, that identif
y the point
of intersection with the other segment */
virtual list<Intersection> intersect(const Segment *s) con
st
{
// std::cerr << "determining intersection of Line wit
h ";
return s->intersectwith(*this);
}

virtual std::eek:stream &print(std::eek:stream &o) const;

virtual void draw(QPainter &qp, double diameter = 0.0) con
st;
virtual std::eek:stream &GCodeDepth(std::eek:stream &o, double d
epth) const;

virtual unsigned char relation(double) const
{ return Segment::CONTINUES_AFTER|Segment::CONTINUES_BEFOR
E; }

/* returns a list of scales relative to the other line, th
at identify
the points of intersection with this line */
list<Intersection> intersectwith(const Line &line) const;

/* returns a list of scales relative to the other Circle,
that identify
the points of intersection with this line */
list<Intersection> intersectwith(const Circle &circle) con
st;

Point P() const { return p; }
Vector D() const { return d; }

double distance(const Point &point) const;

friend std::eek:stream &operator<<(std::eek:stream &o, const Lin
e &l);
};

I'll avoid showing you Line.cpp because it's huge.
Now assume that I wasn't an idiot, everything that has a prototype
in Line.hpp HAS been implemented in Line.cpp.

There is NO definition of a ~Line() destructor ANYWHERE. I
shouldn't need one since the default is fine.

Why am I getting this linker error?????

And just to prove I'm not a total idiot...

This following little test program has the exact same architecture
and it compiles, links and runs just fine...
#include <iostream>

class Base
{
public:
virtual ~Base() {};
virtual int size() const = 0;
};

class Child : public Base
{
int s;
public:
Child(int ss) { s = ss; }
virtual int size() const;
};

int Child::size() const
{
return s;
}

int main(int argc, char *argv[])
{
Child t(56);
Base *b = &t;
std::cout << "Yeah, but I work just fine " <<
b->size() << std::endl;
}

Why won't my bigger program? And to top things off the version I
was working on a couple of weeks ago compiled and linked fine. I
made changes to member functions but didn't touch the
destructors AT ALL. And now it doesn't work.

The only thing I can think of is to rollback all my changes since a
commit of several weeks ago and start over. Because I think I'm
tripping a g++ bug here. The only thing I changed was code in
member functions. I didn't touch constructors, destructors or add
or remove any .h or .cpp files and this bug cropped up and I can't
make it go away. I mean look... Segment.hpp has a destructor
defined and coded, all of the classes either have a destructor
defined or I have not specified a destructor prototype and the
default should be fine and present.

Sigh...
 
D

druberego

It took me a long time to copy the source files and
pair them down to the absolute minimum but I think
I found the answer...
(And I still sort of think it's a g++ bug)

I have it down to one very small file....

test.cpp
-----------------------------------
class Base
{
public:
virtual ~Base(void) {};

virtual unsigned char fun(void) const;
};

class Derived : public Base
{
public:
Derived() {}

virtual unsigned char fun(void) const;
};

unsigned char Derived::fun(void) const
{
return '\0';
}

int main(int argc, char *argv[])
{
Derived e();
}
----------------------------------

When compiled with
$ g++ test.cpp
/tmp/ccwHAAZy.o: In function `Base::~Base()':
test.cpp:(.text._ZN4BaseD2Ev[Base::~Base()]+0xd): undefined reference
to `vtable for Base'
/tmp/ccwHAAZy.o:(.rodata._ZTI7Derived[typeinfo for Derived]+0x10):
undefined reference to `typeinfo for Base'

Which is very similar to my original problem.

Now, Why do I think it's a g++ bug???
A couple of reasons:
1) The error message is misleading. It indicates that
the problem exists in Base::~Base(). which it
does not. The problem lies with the virtual fun()
method. (notice that I failed to provide a body for
fun() in the Base class. Which shouldn't matter
during linking time because I never call fun()
ANYWHERE.
2) There are multiple possible "fixes" and they don't
seem to be consistent In one incarnation I can
make the problem disappear by removing the
Base::~Base() definition entirely. (Though the
extremely short, single file version doesn't seem
to make a difference as it shouldn't.)

Fixes include:
A) specify the Derived::fun() body inline in the
class definition instead of as seperate code.
Huh??? how should that make a difference??
Except from inlining shouldn't the linker have to
link the same stuff and build all the same virtual
table stuff?? (Warning I know nothing about
implementing virtual functionality at the compiler
design level.)
B) Do the correct fix. Make the Base::fun() pure
virtual:
virtual unsigned char fun(void) = 0;

Basically, Isn't this a g++ bug? Should g++/linker warn
that it couldn't find a reference for Base::fun() instead of
complaining about Base::~Base()?

While I know it's wrong of me to neglect to provide a code
definition for anything why should it matter during linking
if Base::fun() is missing if it's never referenced anywhere?

Yes, I know I answered my original question but I sure
would like some insight into the questions the fix raised?
 
V

VJ

Now, Why do I think it's a g++ bug???
A couple of reasons:
1) The error message is misleading. It indicates that
the problem exists in Base::~Base(). which it
does not. The problem lies with the virtual fun()
method. (notice that I failed to provide a body for
fun() in the Base class. Which shouldn't matter
during linking time because I never call fun()
ANYWHERE.

Yes, this error message is really stupid.
2) There are multiple possible "fixes" and they don't
seem to be consistent In one incarnation I can
make the problem disappear by removing the
Base::~Base() definition entirely. (Though the
extremely short, single file version doesn't seem
to make a difference as it shouldn't.)

Fixes include:
A) specify the Derived::fun() body inline in the
class definition instead of as seperate code.
Huh??? how should that make a difference??
Except from inlining shouldn't the linker have to
link the same stuff and build all the same virtual
table stuff?? (Warning I know nothing about
implementing virtual functionality at the compiler
design level.)
B) Do the correct fix. Make the Base::fun() pure
virtual:
virtual unsigned char fun(void) = 0;

Basically, Isn't this a g++ bug? Should g++/linker warn
that it couldn't find a reference for Base::fun() instead of
complaining about Base::~Base()?

That is the most obscure error message the gcc produces, but the reason
is usually simple:

The compiler has to put the vtable into an object file. It puts it into
the object file where the definition of the first non-inline member
function is. If it is missing, you get this rather unhelpful linker
error. Please check the existence of the definitions of your member
functions.

While I know it's wrong of me to neglect to provide a code
definition for anything why should it matter during linking
if Base::fun() is missing if it's never referenced anywhere?

Yes, I know I answered my original question but I sure
would like some insight into the questions the fix raised?

http://gcc.gnu.org/faq.html#vtables
 
V

VJ

test.cpp
-----------------------------------
class Base
{
public:
virtual ~Base(void) {};

virtual unsigned char fun(void) const;
};

class Derived : public Base
{
public:
Derived() {}

virtual unsigned char fun(void) const;
};

unsigned char Derived::fun(void) const
{
return '\0';
}

int main(int argc, char *argv[])
{
Derived e();
}
----------------------------------

Sorry. I forgot the solution. Here it is:

unsigned char Base::fun( ) const
{
// want to do something?
}

Or make it pure virtual.
 
A

Alex Buell

virtual unsigned char fun(void) const;

if you don't want to define a function for this; then do:

virtual unsigned char fun(void) const {};

*or*

virtual unsigned char fun(void) const = 0;

Easy.
 
R

Ron Natalie

int main(int argc, char *argv[])
{
Derived e();
}

In addition to the already answered questions about
the failure to fine Base::fun().. you do know that
the declaration in the main there defines a function
called e returning Derived (not a default constructed
Derived object called e)?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top