PDA

View Full Version : Abstract threading



TheJim01
9th April 2010, 17:02
I'm trying to write a wrapper so threaded classes have a common interface--so I can write threaded classes expecting certain available functionality, and I and others can write clients, expecting some common functionality.

So, I wrote an abstract class to act as my "middle man."

#ifndef THREADBASE_H
#define THREADBASE_H

#include <QThread>

class ThreadBase : public QThread
{
public:
ThreadBase();
signals:
void progressChanged(int);
void exceptionOccurred(int);
protected:
void run();
virtual void runThread() = 0;
};

ThreadBase::ThreadBase() : QThread(0)
{
}

void ThreadBase::run()
{
runThread();
}

#endif // THREADBASE_H
This compiled fine. I then tried writing a derived class.

// testsubclass.h
#ifndef TESTSUBCLASS_H
#define TESTSUBCLASS_H

#include "threadbase.h"

class TestSubClass : public ThreadBase
{
public:
TestSubClass();
protected:
void runThread();
};

#endif // TESTSUBCLASS_H
// testsubclass.cpp
#include "testsubclass.h"

TestSubClass::TestSubClass() : ThreadBase()
{
}

void TestSubClass::runThread()
{
for(int i = 0; i < 100; i++)
{
emit progressChanged(i);
wait(100);
}
emit progressChanged(100);
}
I'm receiving a compiler error on lines 12 and 15 in testsubclass.cpp:

undefined reference to `ThreadBase::progressChanged(int)'
I think I'm having a mind-block, because I have done this before in other classes, and it works. As a derived class, shouldn't TestSubClass have access to the public members of ThreadBase? This might be a C++ question, but I didn't know if there were special rules for using signals in abstract classes that I missed.

P.S. I'm in Windows, using MinGW, with gcc version 3.4.5.

toutarrive
9th April 2010, 17:17
Have you forgot to add Q_OBJECT macro to the header files?

TheJim01
9th April 2010, 21:01
Oddly, no. I had Q_OBJECT in both of my classes, but I received compiler warnings regarding the vtable for each class. Usually those errors mean I forgot to define a pure virtual function, but I should only need to redefine QThread::run() for the abstract class. I removed the Q_OBJECT lines, and the vtable errors went away and were replaced with the undefined errors. If I comment out the offending lines, it compiles with no errors.

toutarrive
9th April 2010, 21:53
You need the Q_OBJECT macro for signals and slots (The undefined reference comes from the fact that you haven't put it in your ThreadBase class declaration).
Regarding the vtable,run make clean, and then qmake follow by make. ( Basically, you have to rebuild your project in order to get rid of the compiler errors for the vtable)

wysota
10th April 2010, 08:31
Please check if you are not reinventing the wheel - QRunnable, QFuture, QFutureWatcher, etc.

TheJim01
12th April 2010, 15:11
You need the Q_OBJECT macro for signals and slots (The undefined reference comes from the fact that you haven't put it in your ThreadBase class declaration).
Regarding the vtable,run make clean, and then qmake follow by make. ( Basically, you have to rebuild your project in order to get rid of the compiler errors for the vtable)One would think (hope) that Creator is smart enough to do that when I select the Build->Clean All followed by Build->Build All. Regardless, I add the Q_OBJECT macro, and used the command line to make clean, qmake, and make. I then got the following errors:

debug/moc_threadbase.o: In function `ZN10ThreadBaseC2Ev':
C:/QtWorkspace/ThreadTester/debug/../threadbase.h:19: multiple definition of `ThreadBase::ThreadBase()'
debug/testsubclass.o:C:/QtWorkspace/ThreadTester/threadbase.h:19: first defined here
debug/moc_threadbase.o: In function `ZN10ThreadBaseC1Ev':
C:/QtWorkspace/ThreadTester/debug/../threadbase.h:19: multiple definition of `ThreadBase::ThreadBase()'
debug/testsubclass.o:C:/QtWorkspace/ThreadTester/threadbase.h:19: first defined here
debug/moc_threadbase.o: In function `ZN10ThreadBase3runEv':
C:/QtWorkspace/ThreadTester/debug/../threadbase.h:24: multiple definition of `ThreadBase::run()'
debug/testsubclass.o:C:/QtWorkspace/ThreadTester/threadbase.h:24: first defined here
debug/moc_testsubclass.o: In function `ZN10ThreadBaseC2Ev':
C:/QtWorkspace/ThreadTester/debug/../threadbase.h:19: multiple definition of `ThreadBase::ThreadBase()'
debug/testsubclass.o:C:/QtWorkspace/ThreadTester/threadbase.h:19: first defined here
debug/moc_testsubclass.o: In function `ZN10ThreadBaseC1Ev':
C:/QtWorkspace/ThreadTester/debug/../threadbase.h:19: multiple definition of `ThreadBase::ThreadBase()'
debug/testsubclass.o:C:/QtWorkspace/ThreadTester/threadbase.h:19: first defined here
debug/moc_testsubclass.o: In function `ZN10ThreadBase3runEv':
C:/QtWorkspace/ThreadTester/debug/../threadbase.h:24: multiple definition of `ThreadBase::run()'
debug/testsubclass.o:C:/QtWorkspace/ThreadTester/threadbase.h:24: first defined here
c:/Qt/2010.01/qt/lib/libqtmaind.a(qtmain_win.o): In function `WinMain@16':
C:\qt-greenhouse\Trolltech\Code_less_create_more\Trollte ch\Code_less_create_more\Troll\4.6\qt\src\winmain/qtmain_win.cpp:93: undefined reference to `_Unwind_Resume'
C:\qt-greenhouse\Trolltech\Code_less_create_more\Trollte ch\Code_less_create_more\Troll\4.6\qt\src\winmain/qtmain_win.cpp:135: undefined reference to `_Unwind_Resume'
c:/Qt/2010.01/qt/lib/libqtmaind.a(qtmain_win.o):C:\qt-greenhouse\Trolltech\Code_less_create_more\Trollte ch\Code_less_create_more\Troll\4.6\qt\src\winmain/../../include/QtCore/../../src/corelib/tools/qvector.h:482: undefined reference to `_Unwind_Resume'
c:/Qt/2010.01/qt/lib/libqtmaind.a(qtmain_win.o):C:\qt-greenhouse\Trolltech\Code_less_create_more\Trollte ch\Code_less_create_more\Troll\4.6\qt\src\winmain/../../include/QtCore/../../src/corelib/tools/qvector.h:483: undefined reference to `_Unwind_Resume'
c:/Qt/2010.01/qt/lib/libqtmaind.a(qtmain_win.o):qtmain_win.cpp:(.eh_fra me+0x12): undefined reference to `__gxx_personality_v0'
collect2: ld returned 1 exit status
make[1]: *** [debug/ThreadTester.exe] Error 1
make[1]: Leaving directory `/c/QtWorkspace/ThreadTester'
make: *** [debug] Error 2Creator gives the first error as:
c:/Qt/2010.01/qt/include/QtCore/../../src/corelib/global/qglobal.h:1368: multiple definition of `ThreadBase::ThreadBase()'These make no sense to me because...

I removed my sub-class and recompiled, successfully. So my abstract base class is correct (I left the Q_OBJECT macro in the code, and I agree I should need it there). I then re-added my "testsubclass" to my project, and recompiled successfully! If I clean the project again, and recompile, I return to the multiple definition errors. The makefile shouldn't be causing this kind of issue, but I suppose it can't hurt to check it. I really don't want to deal with 2-part compiling for this...


Please check if you are not reinventing the wheel - QRunnable, QFuture, QFutureWatcher, etc.I fully agree that re-invention is a burden, and I actually wanted to use the QFuture/QFutureWatcher classes to monitor my thread, but I chose to not use QtConcurrent/QThreadPool for a few reasons, mainly because I don't need to run a function over a set of data--it only runs once--and I need to handle exceptions coming back from the thread.

spud
12th April 2010, 16:38
You need to declare ThreadBase::ThreadBase() and ThreadBase::run() as inline. Since more than one file includes "threadbase.h", there actually are multiple definitions of those functions.

wysota
12th April 2010, 17:05
I fully agree that re-invention is a burden, and I actually wanted to use the QFuture/QFutureWatcher classes to monitor my thread, but I chose to not use QtConcurrent/QThreadPool for a few reasons, mainly because I don't need to run a function over a set of data--it only runs once--and I need to handle exceptions coming back from the thread.
QRunnable doesn't run a function over a set of data. It just runs a function in a thread.

TheJim01
14th April 2010, 16:05
QRunnable doesn't run a function over a set of data. It just runs a function in a thread.

Maybe I'm just confused regarding the use of QRunnable vs. QThread. QThread seems like a more robust interface for threading, where QRunnable is very basic (and I can certainly see applications for it).

I'm not trying to be argumentative--I'm still learning, and just want to understand the differences. :)

TheJim01
14th April 2010, 16:13
You need to declare ThreadBase::ThreadBase() and ThreadBase::run() as inline. Since more than one file includes "threadbase.h", there actually are multiple definitions of those functions.

I always feel silly when these basic solutions pop up. Thank you, I now see where I went wrong, and fixed my code with your suggestion.

wysota
14th April 2010, 17:13
QThread seems like a more robust interface for threading, where QRunnable is very basic (and I can certainly see applications for it).
QThread loses most of its robustness when you remember that the QThread object lives in a different thread than its run() method. To me the only differences between QThread and QRunnable is that QThread is derived from QObject and QRunnable is not and that QThread can run an event loop.

TheJim01
14th April 2010, 18:20
QThread loses most of its robustness when you remember that the QThread object lives in a different thread than its run() method.Which is useful, because one does not have to create a instance pointer to interact directly with the thread. But I see your point. As long as I'm creating and connecting my own signals, the reference doesn't need to exist.


To me the only differences between QThread and QRunnable is that QThread is derived from QObject and QRunnable is not and that QThread can run an event loop.The latter is not relevant to my current needs, and I can certainly replicate or find alternatives the other signals and functions.

If I create a class that inherits from QRunnable, then create (and "new") a pointer of the class in my main (GUI) thread, then try to "start" the thread using QThreadPool::start(myObject), doesn't that conflict with the original idea that an object "lives" in the thread in which it is created? Or does this functionality similar to QObject::moveToThread(QThread *)?

Thanks for your insights!

wysota
14th April 2010, 20:20
Which is useful, because one does not have to create a instance pointer to interact directly with the thread.
You can't interact with the thread directly. With QThread you only interact with its controller.


If I create a class that inherits from QRunnable, then create (and "new") a pointer of the class in my main (GUI) thread, then try to "start" the thread using QThreadPool::start(myObject), doesn't that conflict with the original idea that an object "lives" in the thread in which it is created?
No, because QRunnable is not a descendant of QObject so it doesn't "live" anywhere (there are no events delivered to it).

TheJim01
14th April 2010, 20:44
You can't interact with the thread directly. With QThread you only interact with its controller.Right, sorry, I mis-spoke.


No, because QRunnable is not a descendant of QObject so it doesn't "live" anywhere (there are no events delivered to it).I need the ability to communicate with the object running in the thread (sending it signals, or receiving signals from it), but by the documentation, QRunnable doesn't seem to support this type of interaction. If it doesn't derive from QObject, then I can't use signals/slots. I could inherit from both QObject and QRunnable, but does that not then defeat the purpose of using QRunnable by making a "homeless" object attach to the parent of the QObject?

I apologise for my confusion, but I'm very interested to ensure I am using the right tools for the job.

wysota
14th April 2010, 21:18
I need the ability to communicate with the object running in the thread (sending it signals, or receiving signals from it), but by the documentation, QRunnable doesn't seem to support this type of interaction.
For that you need an event loop. So your previous statement about not needing an event loop could have been premature :)


If it doesn't derive from QObject, then I can't use signals/slots.
No, you can't declare signals and slots. You can have objects that declare them. But signals won't be delivered to them without an event loop (you can still emit signals though)


I could inherit from both QObject and QRunnable
That wouldn't make much sense. You'd be effectively duplicating QThread.

TheJim01
14th April 2010, 21:46
That wouldn't make much sense. You'd be effectively duplicating QThread.Hence my confusion. I saw the relationship, and didn't want to reinvent the wheel to avoid reinventing the wheel! :)


For that you need an event loop. So your previous statement about not needing an event loop could have been premature :)

No, you can't declare signals and slots. You can have objects that declare them. But signals won't be delivered to them without an event loop (you can still emit signals though)Confusion returns. It looks like I missed some critical documentation. I'll have another read, give it another try, and come back if I have more issues.

Thanks again for your time and help!