PDA

View Full Version : undefined reference to vtable in class



JeanC
25th February 2011, 10:24
Hello,

I am trying to use this code and I am getting the error 'undefined reference to `vtable for ClientHandler', on the lines indicated with << on this line
It's supposed to become a simple console program to update my dns subscriptions on namecheap and dyndns.



#include <QDebug>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QNetworkRequest>
#include <QObject>

class ClientHandler : public QObject
{ // << on this line
Q_OBJECT
QNetworkAccessManager *manager;
private slots:
void replyFinished(QNetworkReply *);
public:
void CheckSite(void);
~ClientHandler();
};

void ClientHandler::replyFinished(QNetworkReply *reply) { qDebug() << "DONE"; }
ClientHandler::~ClientHandler() {} // <<-- on this line

void ClientHandler::CheckSite(void)
{
QUrl qrl("http://checkip.dyndns.org");
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
manager->get(QNetworkRequest(qrl));
}


Thanks for help!

high_flyer
25th February 2011, 10:33
This header is probably not getting moc'ed, or the moc result location is not known to the compiler.

stampede
25th February 2011, 10:34
No problems with compiling this code here.
Added this to header:

#include <QDebug>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QNetworkRequest>
#include <QObject>

class ClientHandler : public QObject
{ // << on this line
Q_OBJECT
QNetworkAccessManager *manager;
private slots:
void replyFinished(QNetworkReply *);
public:
void CheckSite(void);
~ClientHandler();
};
This to .cpp:

void ClientHandler::replyFinished(QNetworkReply *reply) { qDebug() << "DONE"; }
ClientHandler::~ClientHandler() {} // <<-- on this line

void ClientHandler::CheckSite(void)
{
QUrl qrl("http://checkip.dyndns.org");
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
manager->get(QNetworkRequest(qrl));
}
Included header in main.cpp and I can use ClientHandler objects:


#include <QApplication>
#include "ClientHandler.h"
int main( int argc, char ** argv ){
QApplication app(argc,argv);
ClientHandler h;
return 0;
}

Then simply: qmake, make - compiled without errors.
Maybe try to do clean rebuild ( make clean, qmake, make ).

JeanC
25th February 2011, 13:18
Thanks to both of you.
Unfortunately I don't understand both of you.
@high_flyer I am rather new to Qt (played with it some years ago) so I don't understand what you mean. I am using Qt Creator so I am not used to moc.
@stampede for the life of me, I don't see what changes you made to the code. I already have a main() in there:



#include <QSettings>
#include <QDebug>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QNetworkRequest>
#include <QObject>

class ClientHandler : public QObject
{
Q_OBJECT
QNetworkAccessManager *manager;
private slots:
void replyFinished(QNetworkReply *);
public:
void CheckSite(void);
~ClientHandler();
};

void ClientHandler::replyFinished(QNetworkReply *reply) { qDebug() << "DONE"; }
ClientHandler::~ClientHandler() {}

void ClientHandler::CheckSite(void)
{
QUrl qrl("http://checkip.dyndns.org");
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
manager->get(QNetworkRequest(qrl));
}

int main(int argc, char *argv[])
{
QSettings ini("Jean", "ddns");
if (!ini.contains("LastIp"))
{
qDebug() << "no ip";
ClientHandler c;
c.CheckSite();
}
return 0;
}

stampede
25th February 2011, 13:25
I have separated the code into three files: header and .cpp for the class, and third file for main() function.
As high_flyer said, the class meta code is not generated, so you are getting undefined references.
I haven't changed anything in the code itself, just when you add the class header to the list of headers in your .pro file, it gets "moc-ed", the Meta-Object code is created in moc_headerName.cpp. You don't have the header file, so this code is not generated.
Split this into three files and add header to project .pro file.
I think you can use the "moc" on your file manually, but splitting it makes more sense to me.
----
Edit:

Split this into three files and add header to project .pro file.
Add all of them to .pro file, .cpp to SOURCES and header to HEADERS

JeanC
25th February 2011, 14:10
Thank you, I understand.
Personally I think it's a bit overkill to use separate files for such a small program, against my taste, but if that's what Qt wants, ok.
Now I tried to add that class in Qt Creator but I can't find how. Off course I could manually edit the .pro file but it seems a bit odd that I cannot do that in Creator. If I try to edit the .pro file in Creator it just ignores my 'File Open'.

I changed things by hand, now I get:
QObject::startTimer: QTimer can only be used with threads started with QThread
and
QObject::connect: Cannot connect (null)::destroyed() to QHostInfoLookupManager::waitForThreadPoolDone()

My guess is that the class is destroyed before it can do anything. How do I avoid that? I'd like to keep this a small console program.

stampede
25th February 2011, 15:38
If I try to edit the .pro file in Creator it just ignores my 'File Open'.
Really ? It opens the .pro for editing when I double click it.
If you really want, you can keep it as one file:

#include <QSettings>
#include <QDebug>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QNetworkRequest>
#include <QObject>

class ClientHandler : public QObject
{
Q_OBJECT
QNetworkAccessManager *manager;
private slots:
void replyFinished(QNetworkReply *);
public:
void CheckSite(void);
~ClientHandler();
};

void ClientHandler::replyFinished(QNetworkReply *reply) { qDebug() << "DONE"; }
ClientHandler::~ClientHandler() {}

void ClientHandler::CheckSite(void)
{
QUrl qrl("http://checkip.dyndns.org");
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
manager->get(QNetworkRequest(qrl));
}

int main(int argc, char *argv[])
{
QSettings ini("Jean", "ddns");
if (!ini.contains("LastIp"))
{
qDebug() << "no ip";
ClientHandler c;
c.CheckSite();
}
return 0;
}

#include "moc_main.cpp"
in order to get "moc_main.cpp" file you need to run "moc":

moc main.cpp -o moc_main.cpp
You don't need to add anything in the pro file this way.
And yes, your ClientHandler goes out of scope and is deleted before finishing the network request:

...
c.CheckSite();
} // c goes out of scope here

How do I avoid that?
One of possible solutions is to use QCoreApplication:


int main(int argc,char**argv){
QCoreApplication app(argc,argv);
QSettings ini("Jean", "ddns");
if (!ini.contains("LastIp"))
{
qDebug() << "no ip";
ClientHandler c;
c.CheckSite();
}
return app.exec();
}

//
void ClientHandler::replyFinished(QNetworkReply *reply) {
qDebug() << "DONE";
qApp->quit();
}

I suppose it would be better to connect to QNetworkReply signals as well, as in example from QNetworkAccessManager docs (http://doc.qt.nokia.com/latest/qnetworkaccessmanager.html#details) ,in order to handle errors.
-----
sorry, now i see the wrong code ( c was deleted anyway ), should be something like:

if (!ini.contains("LastIp"))
{
qDebug() << "no ip";
ClientHandler c;
c.CheckSite();
app.exec();
}

JeanC
26th February 2011, 10:23
You are a great help, thank you!

Yes indeed nothing happens if I doubleclick ddns.pro, really strange..

The project is back to one file again, turned out I had to install libqt4-dev in order to use moc.
And I used QCoreApplication, I finally understand what that's for.
Ater applying your suggestions, it now compiles, runs and even works. :)
I added some errorchecking and the cleaning up for QNetworkReply, I think it's ok.
Now I can go on! (making mistakes :) )
I'm gonna try to make that class into a blocking / synchronous method like it used to be before qt 4.6 I think.

Have a fine day!



void ClientHandler::replyFinished(QNetworkReply *reply)
{
if (reply->error() != 0)
qDebug() << "Error";
else
{
QByteArray a = reply->readAll();
qDebug() << a;
qDebug() << "DONE";
}
reply->deleteLater();
qApp->quit();
}


You are a great help, thank you!

Yes indeed nothing happens if I doubleclick ddns.pro, really strange..

The project is back to one file again, turned out I had to install libqt4-dev in order to use moc.
And I used QCoreApplication, I finally understand what that's for.
Ater applying your suggestions, it now compiles, runs and even works. :)
I added some errorchecking and the cleaning up for QNetworkReply, I think it's ok.
Now I can go on! (making mistakes :) )
I'm gonna try to make that class into a blocking / synchronous method like it used to be before qt 4.6 I think.

Have a fine day!



void ClientHandler::replyFinished(QNetworkReply *reply)
{
if (reply->error() != 0)
qDebug() << "Error";
else
{
QByteArray a = reply->readAll();
qDebug() << a;
qDebug() << "DONE";
}
reply->deleteLater();
qApp->quit();
}


Added after 39 minutes:

I made it a blocking / synchronous class:



#include <QProcess>
#include <QNetworkAccessManager>
#include <QUrl>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QObject>
#include <QCoreApplication>

class Http : public QObject
{
Q_OBJECT
QNetworkAccessManager *manager;
private slots:
void replyFinished(QNetworkReply *);
public:
bool ready;
QString Reply;
QString Get(QString);
~Http() {};
};

void Http::replyFinished(QNetworkReply *reply)
{
if (reply->error() != 0)
{
Reply = "Error";
}
else
{
Reply = reply->readAll();
}
ready = true;
reply->deleteLater();
}

QString Http::Get(QString S)
{
QUrl qrl(S);
manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
manager->get(QNetworkRequest(qrl));
ready = false;
while (!ready)
qApp->processEvents();
return Reply;
}


Useage:



int main(int argc, char *argv[])
{
QCoreApplication app(argc,argv);
Http http;
QString S = http.Get("http://www.someurl.com");
etc.


Feel free to comment.