How to integrate C++ and QML? Registering C++ class as a singleton to QML

Qt Qml development
2022-06-09
8 minutes
Registering C++ class as singleton to QML

Introduction

In our previous post about Registering C++ class as a singleton to QML, we explained why it is important to integrate QML and C++ and covered the basics of this mechanism. In this article, we will go into more detail and discuss a specific real-life example of integrating elements that can be instantiated one-time only (aka. singletons).

If you have come across this post because you are looking for software development help then we encourage you to learn more about our C++ development services.

What is Singleton?

Before we start, let us briefly explain what singleton is, for those who hear this term for the first time. Singleton is a software design pattern that restricts creating an object of a class to a single instance only. There are many use cases for singletons. I.e. an object that holds a program state and can be accessed from different parts of the application.

It is common to utilize this pattern in QML as well. A controller that holds some dynamic (not only) properties like signals and slots and supplies utility functions is a great candidate for a singleton pattern class object. However, you should be careful about using this pattern in applications that apply multi-threading.

Qt framework provides multiple ways of registering C++ classes in QML and each of them has its use. QML Javascript can also be used for this purpose. Today we will discuss the most recent one using qt_add_qml_module(). It was introduced in Qt 6.2 and is widely recommended for registering QML type.

First of all, we need to create a new C++ class we want to expose to QML.

 

#include <QObject>
#include <QtQml/qqml.h>

class ImportantClass : public QObject
{
    Q_OBJECT
    QML_SINGLETON
    QML_ELEMENT
    Q_PROPERTY(int importantNumber READ importantNumber WRITE setImportantNumber NOTIFY importantNumberChanged)

public:
    explicit ImportantClass(QObject *parent = nullptr);

    void setImportantNumber(int num) {
        if (num != m_importantNumber) {
            m_importantNumber = num;
            emit importantNumberChanged();
        }
    }

    int importantNumber() const {
        return m_importantNumber;
    }

public slots:
    double convertFahrenheit(int value) const {
        return value * 1.8 + 32;
    }

signals:
    void importantNumberChanged();

private:
    int m_importantNumber = 10;

};

Everything should look familiar to you, if you have read our previous about Exposing object and register C++ class to QML. The only difference is the Q_SINGLETON macro, it is important to register a singleton type properly. In ImportantClass we define an importantNumber property and a convertFahrenheit(value) function which, as you may guess from the name, converts provided value to Fahrenheit temperature scale.

Next we need to go to our CMakeLists.txt file and use qt_add_qml_module() command to register our class.

 

qt_add_qml_module(appSingletonExample
    URI SingletonExample
    VERSION 1.0
    QML_FILES
        main.qml
    SOURCES
        ImportantClass.h
        ImportantClass.cpp
)

Provide a URI that you would like to use in the QML import statement. Then specify the C++ files your class is in. Congratulations, you have successfully registered a singleton in QML!

Let us create an example to check if everything works correctly. Here is a simple QML application consisting of two buttons.

 

import QtQuick
import QtQuick.Controls

import SingletonExample

Window {
  width: 640
  height: 480
  visible: true
  title: qsTr("Hello World")

  Button {
    id: importantNumberButton

    anchors.centerIn: parent

    width: 100
    height: 50

    text: ImportantClass.importantNumber

    onClicked: {
      ImportantClass.importantNumber += 1
    }
  }

  Button {
    anchors {
      top: importantNumberButton.bottom
      horizontalCenter: importantNumberButton.horizontalCenter
    }

    width: 100
    height: 50

    text: "Click to convert"

    onClicked: {
      text = ImportantClass.convertFahrenheit(ImportantClass.importantNumber)
    }
  }
}

hello world

 

One of them increases the mentioned-above importantNumber value and second calls convertFahrenheit() function on this value. Qt Creator may sometimes complain about wrong imports, but this is not true. Make sure you update Qt Creator to the newest version or reset the QML type model. Then it is more probable that your own QML modules will be recognized properly in your code editor.

In addition, IDE will not provide autocomplete for registered types. We hope it will be fixed soon, because it does not save you from making subtle spelling mistakes which do not stop the execution of QML code, but result in having undefined values.

Next, we need to import the registered type using import URI (import SingletonExample in our case). Now we may use ImportantClass in this QML file: Clicking on the first button increases the value of ImportantNumber by 1:

 

onClicked: {
  ImportantClass.importantNumber += 1
}

And clicking the second button sets its text to the Fahrenheit value of importantNumber:

 

onClicked: {
  text = ImportantClass.convertFahrenheit(ImportantClass.importantNumber)
}

Note, that you do not have to initialize ImportantClass objects as it is done internally. Attempts to do that will result in an error. Remember about the Q_SINGLETON macro? That is the magic word that extends your QML code with C++ singleton visible as QML object.

That is actually it. You have learned how to register singletons in QML from C++ using CMake qt_add_qml_module() command. If you want to learn about integrating QML and C++ we suggest reading official Qt documentation as well as our other blog posts in „How to integrate QML and C++” series.

 

When to Use Singleton Class?

Trying to decide whether the Singleton pattern is appropriate can be difficult. It can seem like a great idea at first, but it can also lead to unexpected issues later on.

 

The Gang of Four recommends utilizing Singleton when:

There should be precisely one instance of a class accessible from a familiar or client access point.

It should be feasible to broaden the single instance through sub-classing without client code amendment.

However, these points may not provide enough practical guidance for assessing if a Singleton is really suitable for your project. You have to see your circumstances.

 

Benefits of Using Singleton C++ Class

 

Global Access Point

A Singleton can act as a global object and provide access from anywhere within the program. But since it is global, it cannot be modified outside of itself. Thus, it serves as a means to safeguard globals from outside interference.

 

Unique Entity Model

Whenever we model real-life entities through our programs, reasoning about them becomes much easier. For instance, registration offices, global timers, and factories responsible for unique IDs are all examples of Singletons.

This creates a correspondence between programming abstractions and reality which helps in simplifying the understanding of the program for clients and coders alike.

 

Disadvantages

  • Global variables can be inadvertently altered in one place and unintentionally used elsewhere, leading to unexpected results.
  • Singletons create a single object, so multiple references can be created for the same instance. This could lead to the tight coupling of classes that rely on global variables, as a change to the data in one class could have implications for another.
  • Unit testing is hindered by Singletons – If an object and its associated methods are too closely linked, it can be difficult to test without a dedicated class for the Singleton.
  • The use of Singletons can also lead to hidden dependencies- As it is readily available in most code bases, and its reference may not be apparent when passed through different methods.

 

To avoid such issues, it is important to ensure that the class you are creating is exactly a Singleton. Also, when designing this pattern with testing in mind, remember to pass the Singleton as a parameter wherever possible using dependency injection. This will help you identify and resolve issues before it becomes too late.

 

Applications of Singleton C++ Classes to QML

Singleton classes have many potential uses, including cache memory, database connections, drivers, and logging. Here are some major applications:

 

Hardware Interface Access

The usage of singleton classes is dependent on the situation and requirements. These objects are often used to prevent concurrent access to a certain class. This is especially useful in hardware resource utilization limitations, such as for hardware printers where the print spooler can be made a singleton to avoid deadlocks arising from multiple simultaneous accesses.

 

Logger

A logger instance created with a singleton class could also prove beneficial when it comes to log files. If there are multiple clients accessing the same logging utility class, issues may occur regarding concurrent access to the same file due to multiple class instances being created. Utilizing a global point of reference via the singleton pattern would help ensure that no two users simultaneously gain access to this utility.

 

Configuration File

Singleton classes are perfect for configuration files because they improve performance by preventing repeated read accesses from multiple users. Moreover, this creates only one instance of the configuration file, which can be accessed concurrently without any data overload or conflict between different reads by various clients from an application’s end.

The data obtained from this single instance would then be utilized repeatedly across all future calls for better memory management and efficiency.

 

Cache

Finally, caches are another type of service that can benefit from using singleton objects as they provide a global point of reference for their hosting applications, which makes accessing stored information easier and more efficient over time with minimal resources required for the operation.

 

Are Singletons Memory Efficient?

When a C++ class is registered as a singleton to QML, it creates a static instance. This is the most commonly used approach. However, it may not be memory efficient. If the class is not called upon, the Singleton will remain present, and if the Singleton is “heavy,” it can take up unnecessary space in RAM.

 

To Sum It Up

Qt QML and C++ pair is the perfect combination for those looking for a flexible, innovative, and reliable way to develop cross-platform applications. With Qt, you can easily deploy your app across multiple platforms with one codebase, ensuring efficient performance and bug-free coding.

Our team of Qt QML development experts can help you every step of the way – from requirements engineering, actual development, debugging, and migration to deployment. With us on your side, you will never have to worry about making a successful application. Try Scythe Studio and see our Qt consulting services – we promise you won’t regret it!

Scythe-Studio - Blog Redactors

Scythe Studio Blog Redactors

Need Qt QML development services?

service partner

Let's face it? It is a challenge to get top Qt QML developers on board. Help yourself and start the collaboration with Scythe Studio - real experts in Qt C++ framework.

Discover our capabilities

Latest posts

[ 134 ]