undefined reference to vtable - virtual member, classes generated by gsoap
Asked Answered
B

2

6

gsoap with its tools wsdl2h and soapcpp2 provided me with a soapStub.h file containing the following:

class SOAP_CMAC ns2__SOAPKunden
{
  public:
    std::string *adresszusatz; 
    // ...
  public:
    virtual int soap_type() const { return 7; }
    // ...
    ns2__SOAPKunden() : adresszusatz(NULL), x(NULL) { }   // left out all member init.
    virtual ~ns2__SOAPKunden() { }
};

I start with a small app using the class to populate objects with data from informix DB.

But to compile successfully i have to leave away all the virtual stuff - i found many postings about this error and use of virtual members in subclasses - otherwise i get

main.o: In function `ns2__SOAPKunden::ns2__SOAPKunden()':
main.cpp:(.text._ZN15ns2__SOAPKundenC1Ev[ns2__SOAPKunden::ns2__SOAPKunden()]+0xf): undefined reference to `vtable for ns2__SOAPKunden'
main.o: In function `ns2__SOAPKunden::~ns2__SOAPKunden()':
main.cpp:(.text._ZN15ns2__SOAPKundenD1Ev[ns2__SOAPKunden::~ns2__SOAPKunden()]+0x13): undefined reference to `vtable for ns2__SOAPKunden'
collect2: ld returned 1 exit status

I admit after years of scripting only it's very hard for me to make sense of C++ code... I want to ask for any advice what to try next. My class is no derived class, is for example what makes me wonder.

Blevins answered 4/1, 2011 at 11:47 Comment(1)
If you are not using derived classes, why is the function virtual?Custard
S
14

The error means that the virtual table has not been correctly compiled/linked in the final binary (executable or library). There are two common circumstances that lead to this error:

  • you are not linking the object file that includes the virtual table definitions --i.e. you compiled soapStub.cpp into soapStub.o, but did not add that binary to the linker command line.
  • the compiler is not generating the virtual table anywhere, so even if you are including all object files, that does not include the virtual table.

The second case is the hardest to identify for non-experienced developers, and can be caused by a class that is defined in the header and contains virtual functions. If all the virtual functions are defined inlined, the compiler will generate the virtual table in all translation units that include the header, and mark it as a weak symbol so that the linker can discard them, but if you later add a new virtual method and you leave it undefined in the header --or if you remove the definition from one of the virtual functions--, then the compiler will not generate the virtual table in each translation unit, but only in the one that defines those functions.

Things to check:

  • you are linking all object files
  • either all virtual functions are defined inline in the class definition or you have a .cpp that defines the virtual functions and you are linking that in.
Stalactite answered 4/1, 2011 at 12:25 Comment(3)
The problem can be that one has declared the destructor in one's derived class, but not defined it. Is that situation covered here?Ruthful
@Moberg: Yes, the second case. Some compilers (gcc among them) will generate the vtable in a single translation unit. GCC will define the vtable only in the translation unit that contains the first (in declaration order) non-inline virtual function, which in many cases will be the destructor (often class definitions start with constructors/destructors)Plauen
Clang has a -Wweak-vtable warning for this (I found out by chance when trying -Weverything on a header-only library)Hectometer
N
1

This is what David Rodriguez said, just stated simpler I guess...

I had this situation in my interface class:

class IBase
{
    public:
    virtual void begin(unsigned long);
    virtual void end();
    virtual int available(void) = 0;
    virtual int peek(void) = 0;
    virtual int read(void) = 0;
    virtual void flush(void) = 0;
}

and changed it to this:

class IBase
{
    public:
    virtual void begin(unsigned long) = 0;
    virtual void end() = 0;
    virtual int available(void) = 0;
    virtual int peek(void) = 0;
    virtual int read(void) = 0;
    virtual void flush(void) = 0;
}

which did the trick.

begin() and end() were defined in derived class in a different file, IBase class (interface) was only declared in header and included in few places.

Error from OP only appeared when I set optimizations to none (-O0), any other setting resulted in no error (gcc 4.8).

Nitrate answered 31/3, 2015 at 3:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.