Search code examples
c++listqtmetadataslot

How to dynamically load datatypes during runtime through a plugin mechanism


Since Qt doesnt allow templates in their slots, I tried to do the following solution without success.

First a would like to create a list like this:

list commands = 
0, "MyDashboard", DashBoard0
1, "MomsDashboard", Dashboard1

Dashboard 0 and 1 are both derived from widget

Lets say you have a QListWidget where we add the strings in the list and then do the connection:

connect(listWidget, SIGNAL(itemClicked(QListWidgetItem*)), 
       this, SLOT(addDashboard(QListWidgetItem*)));

void addDashboard(QListWidgetItem* item) {
    int index = listWidget->row(item);

    QWidget* widget = new typeof(command.atIndex(index).getType());
    widget->show();

}

How would I create that list and store types just like you do in C# Type?


Solution

  • C++ does not allow you to create an object (using the new operator) where the type is only known at runtime. However, you can use a simplified form of the Factory Method pattern as a workaround.

    Here's an example:

    // Type IDs that are associated with a widget type
    enum WidgetTypeId {
        dashboard1WidgetTypeId,
        dashboard2WidgetTypeId
    };
    
    // Factory method
    QWidget* createWidget(WidgetTypeId type) {
        switch (type)
        {
            case dashboard1WidgetTypeId:
                return new DashBoard0;
    
            case dashboard2WidgetTypeId:
                return new DashBoard1;
        }
    }
    
    void addDashboard(QListWidgetItem* item) {
        int index = listWidget->row(item);
    
        QWidget* widget = createWidget(command.atIndex(index).getWidgetTypeId());
        widget->show();
    
    }
    

    Not very pretty, I know. If your widgets are clonable, you can use a std::map instead of the ugly switch statement. This alternative approach would be an example of the Prototype Pattern. Here's some sample code showing this approach:

    class WidgetFactory
    {
    public:
        QWidget* create(const std::string& name) {
            return prototypes_[name]->clone();
        }
    
        void addPrototype(const std::string& name, QWidget* prototype) {
            prototypes_[name] = prototype;
        }
    
    private:
        std::map<std::string, QWidget*> prototypes_;
    }
    
    
    WidgetFactory factory;
    factory.addPrototype("DashBoard0", new DashBoard0);
    factory.addPrototype("DashBoard1", new DashBoard1);
    
    
    void addDashboard(QListWidgetItem* item) {
        int index = listWidget->row(item);
    
        QWidget* widget = factory.create(command.atIndex(index).getWidgetTypeName());
        widget->show();
    
    }
    

    C++ is not a very dynamic language. It has limitied RTTI capabilities and practically none of the reflection features in C#. That's why you have to resort to patterns like Factory Method and Abstract Factory.


    ADDENDUM

    It hasn't dawned on me that Qt might provide runtime class information beyond what is normally available in C++ (I only used Qt for simple utility apps, so I don't know all the bells & whistles available in that framework). With that in mind, I searched and found this mailing list discussion on how to instantiate Qt objects by class name. I don't know if that solution works for plugin objects, though.