Search code examples
c++qttemplatesstructqlist

QList of user templated defined structs


Can I define a QList such that it holds a collection of templated structs, with each struct defined on different type ??

Consider:

template<typename T>struct AA
{
  T value;
}

Can I declare a QList such that it holds different instances of AA ?? something like:

struct<int> myIntStruct;
myIntStruct.value = 10;
struct<double> myDobleStruct;
myDoubleStruct = 12.2;

template<typename T>
QList<struct AA<T>> myList;
myList.push_back(myIntStruct);
myList.push_back(myDoubleStruct);

My Opinion is that the QList should contain entities of same datatype (even for a templated objects) and that's why the above operation is illegal. In that case what structure can I use in Qt to do an operation like that ??

Thanks,

Vishnu.


Solution

  • Why you would want a container to have dissimilar objects is probably a design flaw. However some languages support this such as Smalltalk which has the concept of a Bag Collection that can contain a mix of different object types. Qt may have some sort of bag container class already but I am unaware of it's existence.

    This concept of mixed types in a single container does not work so well in the strongly typed world of C++. Variant objects are the closest thing and even then not for non integral types without making your own "super variant" class which you could do by subclassing QVariant then adding some bits to handle your structs.

    Again ask yourself the question why do you need this functionality? A simpler solution implemented elsewhere will probably alleviate the need for such a container.

    Edit: Quickie example of QMap<QString, QVariant> to help determine if that would work.

    class SomeObject
    {
    public:
        SomeObject() // default
            : someInt(0), someDouble(0) {}
        SomeObject(int i, double d) // explicit
            : someInt(i), someDouble(d) {}
    
        int GetSomeInt() const { return someInt; }
        double GetSomeDouble() const { return someDouble; }
    
    private:
        int someInt;
        double someDouble;
    };
    
    // must be outside of namespace
    Q_DECLARE_METATYPE(SomeObject)
    
    // then you can do stuff like this:
        QMap<QString, QVariant> mapNameToValue;
    
        // populate map
        mapNameToValue["UserName"] = "User";
        mapNameToValue["Port"] = 10101;
        mapNameToValue["PI"] = 3.14159265;
        mapNameToValue["DateTime"] = QDateTime::currentDateTime();
        QVariant userValue;
        userValue.setValue(SomeObject(5, 34.7));
        mapNameToValue["SomeObject"] = userValue;
    
        // read from map
        QString userName = mapNameToValue["UserName"].toString();
        unsigned int port = mapNameToValue["Port"].toUInt();
        double PI = mapNameToValue["PI"].toDouble();
        QDateTime date = mapNameToValue["DateTime"].toDateTime();
        SomeObject myObj = mapNameToValue["SomeObject"].value<SomeObject>();
    
        int someInt = myObj.GetSomeInt();
        double someDouble = myObj.GetSomeDouble();
    

    QVariant handles a lot of types it also has template methods setValue and value<> for user defined types. As shown briefly above you have to use Q_DECLARE_METATYPE macro to register it with QMetaType and provide a default constructor in case value<> conversion fails. It won't support certain comparison operators so I would make my own version of QVariant and QMetaType to play nice with any extra types needed so it feels more consistent.