Decorating pointer variables, this time Qt

From a previous post I have written some might have noticed that I like to decorate pointer to eliminate raw pointer in source code as much a possible. There are several reasons why I like to do this. From some feedback I have received I know that it is not that obvious why I like to do this, or why I write about it, so first something about the why.

Why I like to have explicit types for pointer variables

Say, for example, I care about source code for resource limited devices that have to be up 7x24, if required for several months or even years, and they have to keep humans alive. This code often has some years of history from different developers.

Dealing with such code defines some of my top priorities, where 2 of them are:

  • readability of the code

  • well defined exception with useful information in case of unwanted situations

Looking at the previous example, where I have introduced the observer pointer, or as I prefer to call it, optional_ptr, there are 2 advantages against using a raw pointer.

First, reading the source code. If the optional_ptr is use it is clear what the author of the code had in mind for this pointer. This speeds up readability of code a lot.
Second, in case of a null pointer access, there will be a contract violated that reports what has happened. Much better than a segfault without any information at all.

This has been one example why I like to decorate pointer variables. The not_null from the microsoft GSL library would be on other example, and now I have one more.

This time Qt pointers

Currently I have some additional pointer decorators in mind. This time for Qt pointer.

Me and Qt on embedded devices

A lot of embedded devices these days have pretty complex graphical user interfaces. Tons of options and settings to set, and there are often even more complex and detailed information and data to display.

Qt is often the tool of choice when it comes to GUI on embedded devices and it is not a bad choice.

The Qt parent resource management, new without delete

Qt has a build in resource management for on the heap created objects. In short and simplified, you create a dialog, for example, and add some widgets like buttons or text labels. You tell the widgets that the dialog is their parent object and so the dialog becomes the owner of these elements. When the dialog becomes deleted, it will delete the object it owns.

The problem

Every object has to be Qt object are mostly created on the heap.

So there are a lot of new without a corresponding delete in the source code.

This makes code extrema hard to read. Is this button variable still used or is it an artifact from some previous refactoring that has been forgotten in the code? And does it have a parent?

It is not just hard for humans to reason about those pointer, also static code analyser have their problems which such variables.

Therefore I was thinking about some helpers that ensure that a Qt object has at some given point in time, like leaving a scope, a parent.

A simple helper

The idea is simple. Use a scope guard for a given pointer that checks on destruction that the given pointer has a parent object.

My first thought was that this must be very simple and my first idea was something like this dummy code

class qmanaged_ptr {
public:
 qmanaged_ptr(QObject* o): p{o}{}
 ~qmanaged_ptr(){ /* assert p has a parent */ ;}
private:
  QObject* p;
};

Of course, it turned out that reality is not that simple.

A surprise, different types of parents in Qt

It was somehow surprising, at least for me, to learn that a parent in Qt is not always a QObject. Once it was, but as other legacy object oriented systems that have evolved over time, also Qt has some 'interesting' internals. Well, why not, Qt has quite some history, soon 25 years.

Depending on it’s concrete type, a Qt object instance can have 3 different types of parents that manage the live time of it.

  • QObject

  • QGraphicsItem

  • QGraphicsLayoutItem

There are maybe more, but so far, with the code I worked with, I have found these.

A implementation to check for the right parent

The following is some early state but already works.

It follows the original, simple idea, but creates, depending on the given type, different implementations to check the correct parent property.

Some traits to identify the type of object

To check if a pointer has a parent I need to now its type. So let’s create some type class that tell me the type.

  // since this code shall work with C++11, I add this
  // C++14 has std::enable_if_t and this would not be required
  template<bool B, class T = void >
  using enable_if_t = typename std::enable_if<B,T>::type;


  template<typename T = void>
  using is_a_qgraphicsitem = enable_if_t<
      std::is_base_of<QGraphicsItem, T>::value>;

  template<typename T = void>
  using is_a_qgraphicslayoutitem = enable_if_t<
      std::is_base_of<QGraphicsLayoutItem, T>::value and
      not std::is_base_of<QGraphicsItem, T>::value>;


  template<typename T = void>
  using is_just_a_qobject =  enable_if_t<
       std::is_base_of<QObject, T>::value and
       not std::is_base_of<QGraphicsItem, T>::value and
       not std::is_base_of<QGraphicsLayoutItem, T>::value>;

This code tells me if some given type is a QGraphicsItem, a QGraphicsLayoutItem, or just a QObject.

Checking the right parent property

Having the traits, it is possible to query the right property to see if there is a parent.

I will call this qmanaged_check. It will work for the 3 wanted types and query, depending on the given type, the parent property.

Implementation looks like followed.

  template<typename T, typename = void>
  struct qmanaged_check ;


  template<typename T>
  struct qmanaged_check<T, is_a_qgraphicslayoutitem<T>>
  {
    bool operator()(T* p) {
        return p->parentLayoutItem() != nullptr ;
    }
    using pointer_t = QGraphicsLayoutItem* ;

  } ;

  template<typename T>
  struct qmanaged_check<T, is_a_qgraphicsitem<T>>
  {
    bool operator()(T* p) {
        return p->parentItem() != nullptr ;
    }
    using pointer_t = QGraphicsItem* ;

  } ;



  template<typename T>
  struct qmanaged_check<T, is_just_a_qobject<T>>
  {
    bool operator()(T* p) {
        return p->parent() != nullptr ;
    }

    using pointer_t = QObject* ;
  } ;

The scope guard

The scope guard uses the check from above in the destructor to notifies at runtime if the assumption of a parent is not true.

The implementation looks something like this.

  template<typename T>
  class qmanaged_ptr
  {

    public:

    qmanaged_ptr(T* t): qptr(t){}

    operator T* () const { return qptr; }

    T* get() const { return qptr; }

    T* reset(T* val = nullptr) const {
      std::swap(qptr, val) ;
      return val;
    }

    bool operator==(T* p) const noexcept
    {
      return qptr == p ;
    }

    bool operator!=(T* p) const noexcept
    {
      return !(*this == p) ;
    }

    void clear()
    {
      qptr = nullptr ;
    }

    ~qmanaged_ptr()
    {
      if(not qmanaged_check<T>()(qptr))
      {
        throw "TODO, some assertion" ; // replace as needed
      }
    }

    T* operator->() const { return qptr; }

    private:
      T* qptr ;

  };


  template<typename T, typename... Args>
  qmanaged_ptr<T> make_qmanaged(Args&&... args) {
      return qmanaged_ptr<T>(new T(std::forward<Args>(args)...));
  }

That’s it.

A qmanaged_ptr<GraphicsWidget> mywidget = make_qmanaged<MyGfxWidget>(…​) can be used like it would be a pointer to a GraphicsWidget, and when mywidget goes out of scope it will make a lot of noise if the widget pointer does not have a parent.

Theory and praxis

That’s the theory. It works good with some example code, but as mentioned, currently this is an idea at early state.

The next weeks will show how this will behave in praxis and in real code.
I am super curious for the results but also optimistic that both, the readability and the guarantee of assumptions of the code, will improve.

Thanks for reading

As usual, if you find any mistakes, in the code or in my spelling, please let me know.
Ideas for improvements, or any other feedback, is also very much welcome.