Click here to Skip to main content
13,900,534 members
Click here to Skip to main content
Add your own
alternative version


2 bookmarked
Posted 20 Feb 2019
Licenced LGPL3

Strongly Typed Identifiers with Qt

, 20 Feb 2019
Rate this:
Please Sign up or sign in to vote.
Strongly Typeded Identifiers in Qt


The idea for this article came after completing a refactoring to an eight year old project. As I tried to better understand the C++/Qt codebase to accommodate a new feature, I decided to replace the int used to identify an Element with an object, ElementId.

You will find this concept under the name of "primitive obsession", we are using an int to represent an identifier, making it difficult to differentiate between real ints and identifiers.

In this article, we will see a possible implementation using the Qt Framework.


You should know C++ and have some knowledge of the Qt Framework.

Setting Up the Basics

Let's have a look at the Element class:

class Element
    int id() const
        { return m_id; }

    int m_id;

The first step was to add a new class providing a thin wrapper over the int value, named ElementId.

class ElementId
    explicit ElementId(int value)
        : m_value(value)
    int value() const;
    int m_value;

After that, I just let the compiler find the errors. The amount of errors can be discouraging, it's not unusual to get a few hundreds. After sorting the errors by type, I had a look at each one. The first problem I found was that the type wasn't always an int, sometimes it will be an uint or an int32.


The second problem was to agree on what a null or valid means. The validation was made in different ways:

if (id)
if (id != 0)
if (id > 0)

So let's make one definition for a valid identifier and add it to our class:

class ElementId
        : m_value(0)
    bool isNull() const
        { return m_value == 0; }
    bool operator!() const
        { return isNull(); }
    operator bool() const
        { return !isNull(); }

The two operators are not necessary, but if we have them, we can write statements like:

if (id) // the id is not null
if (!id) // the id is null

Other required operators will be the ones for comparison and assignment. Since they are not special, I will not show them here, but you can check the attached project for the complete definition.

Supporting QVariant

Sometimes, the identifiers will be stored as QVariant data in a QTreeViewItem or a QComboBox. If the identifiers need to be stored in a database, the Qt classes will also use the QVariant type. The first step is to make our type known to QVariant:


// register the type (typically in main.cpp)

This allows us to use the new class like:

ElementId e(10);
QVariant v = QVariant::fromValue(e);
ElementId e2 = v.value<ElementId>();
Q_ASSERT(e == e2);

Of course, one can also add methods like toVariant() and fromVariant().

Another nice thing about having a QVariant is that we can also use the QVariant's methods like toString() or toInt(). We let Qt know how the conversion should be done:

class ElementId
    QString toString() const
        { return QString::number(m_value); }

QMetaType::registerConverter<ElementId, QString>(&ElementId::toString);
QMetaType::registerConverter<ElementId, int>(&ElementId::value);

To complete our support for QVariant, we can also add stream operators:

QDataStream& operator<<(QDataStream& out, const ElementId& id)
    out << (qint32) id.value();
    return out;

QDataStream& operator>>(QDataStream& in, ElementId& id)
    qint32 value;
    in >> value;
    id = ElementId(value);

// register the operators for QMetaType

Supporting QList, QSet or QHash

Using the new type in a QList will behave as working with ints, no surprises here. However, the Q_DECLARE_TYPEINFO can be used to help the Qt's generic containers to choose the appropriate storage methods:


To use the new type as key in associative containers, we will need to provide a specialization for the hashing function. Since the only member of our class is an int, the existing implementation to hash an integer is used.

int qHash(const ElementId& id) 
    return qHash(id.value()); 

Supporting QDebug

The streaming operator will check for a valid identifier, for a null identifier, "null" will be written.

QDebug operator<<(QDebug debug, const ElementId& id)
    QDebugStateSaver saver(debug);
    debug << "ProductId" << '(';
    if (productId.isNull())
        debug.noquote() << "null";
        debug << productId.value();
    debug << ')';
    return debug;


We introduced a new value type to hold a product identifier, that provides basic validation and QVariant support.


  • 21st February, 2019: Initial version


This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


About the Author

Aurelian Georgescu
Software Developer (Senior)
Austria Austria
No Biography provided

You may also be interested in...

Comments and Discussions

-- There are no messages in this forum --
Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web03 | 2.8.190306.1 | Last Updated 21 Feb 2019
Article Copyright 2019 by Aurelian Georgescu
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid