Results 1 to 14 of 14

Thread: Problem with Qt plugin system

  1. #1
    Join Date
    Mar 2009
    Posts
    72
    Thanks
    7
    Qt products
    Qt4 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Problem with Qt plugin system

    Hi, this is my first post here

    I'm trying to realize an embedded application framework based on Qt 4.5 (Qt Embedded for Linux) similar to Qtopia/QtExtended.

    I want to implement a dynamic loader for third-party applications, so I'm using the Qt plugin system. I have my application interface class:

    Qt Code:
    1. class pkApplication
    2. {
    3. public:
    4. enum { InvalidId = 0 };
    5.  
    6. public:
    7. virtual ~pkApplication() {};
    8.  
    9. virtual unsigned int id() const = 0;
    10. virtual bool startUp(QWidget *parent = 0) = 0;
    11. };
    12.  
    13. Q_DECLARE_INTERFACE(pkApplication, "it.card-tech.pk.pkApplication/1.0");
    To copy to clipboard, switch view to plain text mode 

    Then, my base application mixin class (it implements application low-level logic and it inherits from QObject):

    Qt Code:
    1. class pkApplicationBase : public QObject, public pkApplication
    2. {
    3. Q_OBJECT
    4.  
    5. private:
    6. static unsigned int s_idCounter;
    7. static QList<unsigned int> s_freeIds;
    8. unsigned int m_id;
    9.  
    10. public:
    11. pkApplicationBase();
    12. virtual ~pkApplicationBase();
    13.  
    14. unsigned int id() const;
    15.  
    16. private:
    17. unsigned int nextFreeId();
    18. };
    To copy to clipboard, switch view to plain text mode 

    And, at the end, my concrete example application:

    Qt Code:
    1. class ExampleApp : public pkApplicationBase
    2. {
    3. Q_OBJECT
    4. Q_INTERFACES(pkApplication)
    5.  
    6. public:
    7. ExampleApp();
    8.  
    9. bool startUp(QWidget* parent);
    10.  
    11. QDialog* w;
    12. };
    To copy to clipboard, switch view to plain text mode 

    (with Q_EXPORT_PLUGIN2(exampleapp, ExampleApp) line in the source file).

    This configuration doesn't work even if it respects pure interface and no-qobject-inheritance (on pkApplication) rules.

    If I inherit ExampleApp directly from pkApplication everything works (obviously with some changes here and there).

    Could you help me to find a solution for this problem, please?

  2. #2
    Join Date
    Jan 2006
    Location
    travelling
    Posts
    1,116
    Thanks
    8
    Thanked 127 Times in 121 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Problem with Qt plugin system

    Quote Originally Posted by zuck View Post
    This configuration doesn't work even if it respects pure interface and no-qobject-inheritance (on pkApplication) rules.

    If I inherit ExampleApp directly from pkApplication everything works (obviously with some changes here and there).
    Wow, you get the exact opposite of the expected behavior There must something weird with your project layout. Oh, by the way, would you mind defining "doesn't work", "everything work" and "some changes here and there", that might help us figuring out what's wrong.

    Quote Originally Posted by zuck View Post
    Could you help me to find a solution for this problem, please?
    Maybe but a more detailed explanation is needed (project layout, test you made, expected results, actual results and such...)
    Current Qt projects : QCodeEdit, RotiDeCode

  3. #3
    Join Date
    Jan 2006
    Location
    travelling
    Posts
    1,116
    Thanks
    8
    Thanked 127 Times in 121 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Problem with Qt plugin system

    Qt Code:
    1. class pkApplicationBase : public QObject, public pkApplication
    2. {
    3. Q_OBJECT
    4. Q_INTERFACES(pkApplication)
    5.  
    6. private:
    7. static unsigned int s_idCounter;
    8. static QList<unsigned int> s_freeIds;
    9. unsigned int m_id;
    10.  
    11. public:
    12. pkApplicationBase();
    13. virtual ~pkApplicationBase();
    14.  
    15. unsigned int id() const;
    16.  
    17. private:
    18. unsigned int nextFreeId();
    19. };
    To copy to clipboard, switch view to plain text mode 

    Qt Code:
    1. class ExampleApp : public pkApplicationBase
    2. {
    3. Q_OBJECT
    4.  
    5. public:
    6. ExampleApp();
    7.  
    8. bool startUp(QWidget* parent);
    9.  
    10. QDialog* w;
    11. };
    To copy to clipboard, switch view to plain text mode 

    It may or may not matter but I think the above is more correct than your original code (the Q_INTERFACE macro should be placed in the first class of the inheritance chain that actually reimplement the interface).

    By the way, the "pure interface" rule matters only if your plugins do not use Qt themselves (which may happen but is generally unlikely and not your case obviously), otherwise inheriting from Qt classes is fine. The real point is that your base plugin class does not bring new methods/members or you would have to put it in a shared lib and both the app and the plugins would have to link against that lib.
    Current Qt projects : QCodeEdit, RotiDeCode

  4. #4
    Join Date
    Mar 2009
    Posts
    72
    Thanks
    7
    Qt products
    Qt4 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Re: Problem with Qt plugin system

    Quote Originally Posted by fullmetalcoder View Post
    Wow, you get the exact opposite of the expected behavior There must something weird with your project layout.
    Why? If ExampleApp inherits directly from pkApplication the situation is similar to EchoPlugin example in the Qt documentation, isn't it?

    Oh, by the way, would you mind defining "doesn't work", "everything work" and "some changes here and there", that might help us figuring out what's wrong.

    Maybe but a more detailed explanation is needed (project layout, test you made, expected results, actual results and such...)
    Yes, sure, i'm sorry

    I have a server application (pkServer) which inherits from QApplication. It has a loadApps() method. With this method the server should load and startup all plugins (third-party applications). Each plugin should inherits from pkApplication interface.

    For "everything works" I mean that after loadApps execution, ExampleApp is correctly loaded and its main window appears on the top of pkServer "desktop" window. With the previous layout, instead, no example window appears because plugin loading fails (it returns a null pointer instead of a pkApplication pointer). This is the body of loadApps method (which is very similar to Qt documentation one and I think the problem is not here):

    Qt Code:
    1. QList<pkApplication*> pkServer::loadApps()
    2. {
    3. QList<pkApplication*> loaded_apps;
    4. QDir pluginsDir(this->applicationDirPath());
    5.  
    6. foreach (QString filename, pluginsDir.entryList(QDir::Files | QDir::NoSymLinks))
    7. {
    8.  
    9. QPluginLoader plugin(pluginsDir.absoluteFilePath(filename));
    10. pkApplication* app = qobject_cast<pkApplication*>(plugin.instance());
    11. if (app)
    12. {
    13. app->startUp(m_desktop);
    14. loaded_apps.append(app);
    15. }
    16. }
    17.  
    18. return loaded_apps;
    19. }
    To copy to clipboard, switch view to plain text mode 

    and this is a simple startUp() method implementation for ExampleApp:

    Qt Code:
    1. bool ExampleApp::startUp(QWidget* parent)
    2. {
    3. w = new QDialog(parent);
    4. w->setWindowTitle("Example");
    5. w->show();
    6.  
    7. return true;
    8. }
    To copy to clipboard, switch view to plain text mode 
    Last edited by zuck; 18th March 2009 at 22:19.

  5. #5
    Join Date
    Jan 2006
    Location
    travelling
    Posts
    1,116
    Thanks
    8
    Thanked 127 Times in 121 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Problem with Qt plugin system

    Quote Originally Posted by zuck View Post
    Why? If ExampleApp inherits directly from pkApplication the situation is similar to EchoPlugin example in the Qt documentation, isn't it?
    Maybe "expected" wasn't the right word... "intuitive" would have been a wiser choice I guess.

    Quote Originally Posted by zuck View Post
    plugin loading fails (it returns a null pointer instead of a pkApplication pointer).
    Then I suppose my previous comment was accurate : QPluginLoader tries to cast the object to pkApplication using qobject_cast<> but as pkApplicationBase does not have the Q_INTERFACE macro this fails (qobject_cast does not work like dynamic_cast, it uses meta object data to check the validity of the cast and then "brute force" casting if the checks succeed).
    Current Qt projects : QCodeEdit, RotiDeCode

  6. #6
    Join Date
    Mar 2009
    Posts
    72
    Thanks
    7
    Qt products
    Qt4 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Re: Problem with Qt plugin system

    Quote Originally Posted by fullmetalcoder View Post
    Then I suppose my previous comment was accurate : QPluginLoader tries to cast the object to pkApplication using qobject_cast<> but as pkApplicationBase does not have the Q_INTERFACE macro this fails
    Yes but if I move the Q_INTERFACES macro into the pkApplicationBase body nothing changes (no example window appears when i launch the server application) and the pointer is always null.

    This is the error string returned by QPluginLoader:

    Qt Code:
    1. Cannot load library [...]/libexampleapp.so: ([...]/libexampleapp.so: undefined symbol: _ZN17pkApplicationBase16staticMetaObjectE)
    To copy to clipboard, switch view to plain text mode 
    Last edited by zuck; 18th March 2009 at 23:58.

  7. #7
    Join Date
    Jan 2006
    Location
    travelling
    Posts
    1,116
    Thanks
    8
    Thanked 127 Times in 121 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Problem with Qt plugin system

    Because that is a typical example of breaking the "(almost) pure virtual interface". There is an issue with your design I think.

    What about something like this :

    Qt Code:
    1. class pkApplication
    2. {
    3. public:
    4. enum { InvalidId = 0 };
    5.  
    6. public:
    7. virtual ~pkApplication() {};
    8.  
    9. virtual void setId(unsigned int id) = 0;
    10. virtual unsigned int id() const = 0;
    11. virtual bool startUp(QWidget *parent = 0) = 0;
    12. };
    13.  
    14. #define PK_APPLICATION \
    15. int m_id; \
    16. void setId(unsigned int id) { m_id = id; } \
    17. unsigned int id() const { return m_id; } \
    To copy to clipboard, switch view to plain text mode 

    Qt Code:
    1. class ExampleApplication : public QObject, public pkApplication
    2. {
    3. Q_OBJECT
    4. Q_INTERFACES(pkApplication)
    5.  
    6. PK_APPLICATION
    7.  
    8. public:
    9. ExampleApp();
    10.  
    11. bool startUp(QWidget* parent);
    12.  
    13. QDialog* w;
    14.  
    15. };
    To copy to clipboard, switch view to plain text mode 
    It should work just fine. Alternatively you may consider removing the id thingy (and using another structure like a QHash to still keep that for your app to use) from the pkApplication as the plugins probably don't need to know anything about it.
    Current Qt projects : QCodeEdit, RotiDeCode

  8. #8
    Join Date
    Mar 2009
    Posts
    72
    Thanks
    7
    Qt products
    Qt4 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Re: Problem with Qt plugin system

    Quote Originally Posted by fullmetalcoder View Post
    Alternatively you may consider removing the id thingy (and using another structure like a QHash to still keep that for your app to use) from the pkApplication as the plugins probably don't need to know anything about it.
    Ok, for this time I can remove "Id" stuff...but if I need to share some concrete methods (which the pkServer instance needs to know too) with all applications derived from pkApplication interface? How can I do that?

    Oh, by the way, thanks for your help!

  9. #9
    Join Date
    Jan 2006
    Location
    travelling
    Posts
    1,116
    Thanks
    8
    Thanked 127 Times in 121 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Problem with Qt plugin system

    Quote Originally Posted by zuck View Post
    if I need to share some concrete methods (which the pkServer instance needs to know too) with all applications derived from pkApplication interface? How can I do that?
    it is recommended to avoid that as much as possible. Your plugin system should be designed (mostly) one-way : the application call methods from the plugin and the plugin acts on its own.
    For the cases where it is really needed, there are two main ways :

    • passing pointers to "controllers" object (recommened method in most cases, more on this below)
    • putting all the common code in a shared lib. both the app and the plugins would link against it

    A controller object is basically a class that allows the plugin to obtain data or to perform actions in a way that is not "predictable" by the app.

    Just create another abstract class exposing methods of interest (which must be pure virtual). Then subclass that class in your app to provide actual implementation of these methods and pass a pointer to an object of your controller class to the plugins. e.g :

    Qt Code:
    1. class pkApplicationController
    2. {
    3. public:
    4. virtual ~pkApplicationController() {}
    5.  
    6. virtual QString statusMessage() const = 0;
    7. virtual void setStatusMessage(const QString& s) = 0;
    8. };
    To copy to clipboard, switch view to plain text mode 

    Qt Code:
    1. class pkConcreteAppController : public pkApplicationController
    2. {
    3. public:
    4. QString statusMessage() const { return m_status; }
    5. void setStatusMessage(const QString& s) { m_status = s; }
    6. };
    To copy to clipboard, switch view to plain text mode 

    Qt Code:
    1. // somewhere in your app : (pointerToController is a pkConcreteAppController*)
    2. plugin->someMethod(pointerToController);
    To copy to clipboard, switch view to plain text mode 

    Qt Code:
    1. // in a plugin
    2. void SomePlugin::someMethod(pkApplicationController *c)
    3. {
    4. if ( !happy )
    5. c->setStatusMessage("Always look at the bright side of life.");
    6. }
    To copy to clipboard, switch view to plain text mode 

    I hope this helps.
    Current Qt projects : QCodeEdit, RotiDeCode

  10. The following user says thank you to fullmetalcoder for this useful post:

    zuck (19th March 2009)

  11. #10
    Join Date
    Mar 2009
    Posts
    72
    Thanks
    7
    Qt products
    Qt4 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Re: Problem with Qt plugin system

    Yes, this is a solution.

    What about using QLibrary instead of QPluginLoader? With QLibrary can I have an abstract base class (no pure virtual methods only) as "parent" for all derived applications?
    Last edited by zuck; 19th March 2009 at 22:10.

  12. #11
    Join Date
    Jan 2006
    Location
    travelling
    Posts
    1,116
    Thanks
    8
    Thanked 127 Times in 121 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Problem with Qt plugin system

    using QLibrary instead of QPluginLoader offers a single avantage : more control. QPluginLoader actually uses a QLibrary under the hood so the same restrictions apply. Besides, using QLibrary implies recreating what QPluginLoader provides (you cannot create objects directly from shared libs, you have to export a "creator" functions which must have a name known to the plugin loading code, must be mangled properly, ...)

    Just to clarify about the requirements on a "base class" for plugins (provided you don't use a "common shared lib" approach) :

    • such a class does not need to be pure virtual (aka abstract) stricto sensu (it can inherit non-abstract classes for instance)
    • it must however be pure virtual in a "practical sense", which basically means that the only methods it brings (on its own, see previous remark) must be pure virtual and it cannot have member variables of its own

    An important consequence is that such a class can inherit QObject for instance but then the Q_OBJECT macro must be omitted as it adds members/methods used by the meta object system.
    Current Qt projects : QCodeEdit, RotiDeCode

  13. #12
    Join Date
    Mar 2009
    Posts
    72
    Thanks
    7
    Qt products
    Qt4 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Re: Problem with Qt plugin system

    So I can write the inverse of original situation:

    Qt Code:
    1. class pkApplicationBase : public QObject
    2. {
    3. // The id logic here (without Q_OBJECT macro).
    4. };
    5.  
    6. class pkApplication : public pkApplicationBase
    7. {
    8. // abstract interface here.
    9. };
    To copy to clipboard, switch view to plain text mode 

    Is it correct?

  14. #13
    Join Date
    Jan 2006
    Location
    travelling
    Posts
    1,116
    Thanks
    8
    Thanked 127 Times in 121 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Problem with Qt plugin system

    No. You can afford to use "semi-abstract" classes as base class for plugins because both your app and the plugin links to Qt libs dynamically (or any other shared lib providing classes that you could be tempted to use as ancestors of your plugin base class).

    Any class that brings at least one method/member variable of its very own (i.e not through inheritance of a class defined in a common shared lib) is definitely unreachable from plugins (unless, again, you move it to a common shared lib).

    Please not that the shared is equally important as the common. You may manage to get it working in some cases when compiling a common lib statically but it is extremely likely to fail miserably or a reason or another in a vast majority of cases.

    In any case you're safer using only (totally) pure virtual classes in your plugin system and, if needed, a bit of meta-object magic here and there.
    Current Qt projects : QCodeEdit, RotiDeCode

  15. #14
    Join Date
    Mar 2009
    Posts
    72
    Thanks
    7
    Qt products
    Qt4 Qt/Embedded
    Platforms
    Unix/X11 Windows

    Default Re: Problem with Qt plugin system

    So the correct procedure is:

    1) Put pkApplication and pkApplicationBase in a shared lib.
    2) Link ExampleApp and pkServer modules to the shared lib.

    ...Right?

Similar Threads

  1. Threads in Designer plugins
    By hvengel in forum Qt Tools
    Replies: 2
    Last Post: 3rd January 2009, 19:19
  2. QtPlugins how to communicate between plugins?
    By BeS in forum Qt Programming
    Replies: 1
    Last Post: 17th December 2008, 11:59
  3. Container plugins
    By Benne Gesserit in forum Qt Tools
    Replies: 4
    Last Post: 20th November 2008, 23:43
  4. Replies: 1
    Last Post: 31st October 2008, 14:35
  5. Qt Plugins Error
    By nathanpackard in forum Qt Programming
    Replies: 1
    Last Post: 17th August 2007, 23:19

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.