[Interest] Heavily Commented Example: Simple Single Frontend with Two BackendsLow,
d3fault
d3faultdotxbe at gmail.com
Fri Oct 26 11:45:03 CEST 2012
tl;dr: How can I use the Q_OBJECT macro within a pre-processor macro?
So I've got the rewrite 99% done, and I mean it looks a heck of a lot
cleaner than the first example. Fixed a lot of typos in comments too.
That 1% is proving to be a problem though :(.
If I don't use that "template" idea I was talking about, then it works
easy. The thing is, I don't want to ever have to sub-class QThread.
Why? Simple: Business logic does not belong in a QThread sub-class. I
want it to be re-usable to the point where I don't have to sub-class
QThread for every object type.
So nao I'm asking for halp.
Templates and MOC don't play nice together. The only hacky workaround
I got working with templates/moc required me to sub-class a templated
class, guh. Might as well sub-class QThread.
I tried with a pre-processor macro, but the Q_OBJECT macro within my
macro messes things up :(.
The only problem I'm having is one of string substitution, lmfao.
Here's what my non-working macro currently looks like:
#define DECLARE_INTENT_TO_USE_OBJECT_ON_THREAD(UserObjectType) \
class UserObjectType##OnThreadHelper : public QThread \
{ \
Q_OBJECT \
public: \
explicit UserObjectType##OnThreadHelper(QObject *parent = 0) :
QThread(parent) { } \
protected:
virtual void run() \
{ \
UserObjectType qobj; \
emit object##UserObjectType##IsReadyForConnectionsOnly(&qobj); \
exec(); \
} \
signals: \
void object##UserObjectType##IsReadyForConnectionsOnly(&qobj); \
};
^^^I hope the formatting doesn't mess up when I post this. I am
escaping all the newlines properly.
Use:
In your header, after #include'ing both the macro and the QObject
inherited types you want to work with on backend threads, you do:
DECLARE_INTENT_TO_USE_OBJECT_ON_THREAD(SimpleBackend1)
DECLARE_INTENT_TO_USE_OBJECT_ON_THREAD(SimpleBackend2)
//etc. Way easier than overriding QThread::run() for each type...
Which creates the classes that you then add to your class as members
(the one that "hasA" QThread/backend-object... in my example: the
"test" class):
private:
SimpleBackend1OnThreadHelper m_Backend1ThreadHelper;
SimpleBackend2OnThreadHelper m_Backend2ThreadHelper;
then connect and start:
//in my "test" class's constructor
connect(&m_Backend1ThreadHelper,
SIGNAL(objectSimpleBackend1IsReadyForConnectionsOnly(SimpleBackend1*)),
this, SLOT(handleSimpleBackend1ReadyForConnectionsOnly(SimpleBackend1*)));
m_Backend1ThreadHelper.start();
connect(&m_Backend2ThreadHelper,
SIGNAL(objectSimpleBackend2IsReadyForConnectionsOnly(SimpleBackend2*)),
this, SLOT(handleSimpleBackend2ReadyForConnectionsOnly(SimpleBackend2*)));
m_Backend2ThreadHelper.start();
and to stop:
//handleAboutToQuit or similar
m_Backend1ThreadHelper.quit();
m_Backend2ThreadHelper.quit();
m_Backend1ThreadHelper.wait();
m_Backend2ThreadHelper.wait();
...which causes the object of the type that you pass into the macro to
go out of scope after exec() returns. Qt's signals/slots handles the
object's disappearance with no problem (signals/slots are smart,
woot).
But that's the thing: the macro doesn't work because of the Q_OBJECT
macro within it. I also tried Q_##OBJECT to try to get it to evaluate
twice... except that doesn't work because MOC runs before the
preprocessor does.
I could trivially add a build step that does string replacing, but
then it isn't very portable/re-usable :(.
Would be nice to be able to do clean threading with just:
#include "objectonthreadhelper.h" //the macro
DECLARE_INTENT_TO_USE_OBJECT_ON_THREAD(MyCustomObjectType)
MyCustomObjectTypeThreadHelper m_MyCustomObjectTypeThreadHelper;
connect(&m_Backend2ThreadHelper,
SIGNAL(objectMyCustomObjectTypeIsReadyForConnectionsOnly(MyCustomObjectType*)),
this, SLOT(handleMyCustomObjectTypeReadyForConnectionsOnly(MyCustomObjectType*)));
m_MyCustomObjectTypeThreadHelper.start();
No more .moveToThread()... which doesn't let your object's constructor
run on the thread
No more overriding QThread::run()... business logic does not belong in
there... and I don't want to do copy/paste programming for every
object type I want on a backend thread
Anyone have a portable solution to that string substitution problem?
d3fault
More information about the Interest
mailing list