[Interest] Heavily Commented Example: Simple Single Frontend with Two BackendsLow,

Bo Thorsen bo at fioniasoftware.dk
Fri Oct 26 12:50:23 CEST 2012


Den 26-10-2012 11:45, d3fault skrev:
> 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?

Have you tried a double base class solution to this?

class WorkerThreadBase : public QObject {
   Q_OBJECT

signals: ...
slots: ...
};

template <typename T> class WorkerThread : public WorkerThreadBase {
   // No Q_OBJECT
...
};

This is a common pattern when you need a class that's template based but 
at the same time isn't. Polymorphism of template classes can be done 
this way, for example.

Bo Thorsen.

Come by my DevDays talk in Berlin - "Designing for testability". Learn how to build and run your unit tests with the Qt application.

Fionia Software - Qt experts for hire.




More information about the Interest mailing list