Trusted Software Excellence across Desktop and Embedded
Take a glance at the areas of expertise where KDAB excels ranging from swift troubleshooting, ongoing consulting and training to multi-year, large-scale software development projects.
Find out why customers from innovative industries rely on our extensive expertise, including Medical, Biotech, Science, Renewable Energy, Transportation, Mobility, Aviation, Automation, Electronics, Agriculture and Defense.
High-quality Embedded Engineering across the Stack
To successfully develop an embedded device that meets your expectations regarding quality, budget and time to market, all parts of the project need to fit perfectly together.
Learn more about KDAB's expertise in embedded software development.
Where the capabilities of modern mobile devices or web browsers fall short, KDAB engineers help you expertly architect and build high-functioning desktop and workstation applications.
Extensible, Safety-compliant Software for the Medical Sector
Create intelligent, patient-focused medical software and devices and stay ahead with technology that adapts to your needs.
KDAB offers you expertise in developing a broad spectrum of clinical and home-healthcare devices, including but not limited to, internal imaging systems, robotic surgery devices, ventilators and non-invasive monitoring systems.
Building digital dashboards and cockpits with fluid animations and gesture-controlled touchscreens is a big challenge.
In over two decades of developing intricate UI solutions for cars, trucks, tractors, scooters, ships, airplanes and more, the KDAB team has gained market leading expertise in this realm.
Build on Advanced Expertise when creating Modern UIs
KDAB assists you in the creation of user-friendly interfaces designed specifically for industrial process control, manufacturing, and fabrication.
Our specialties encompass the custom design and development of HMIs, enabling product accessibility from embedded systems, remote desktops, and mobile devices on the move.
Legacy software is a growing but often ignored problem across all industries. KDAB helps you elevate your aging code base to meet the dynamic needs of the future.
Whether you want to migrate from an old to a modern GUI toolkit, update to a more recent version, or modernize your code base, you can rely on over 25 years of modernization experience.
KDAB offers a wide range of services to address your software needs including consulting, development, workshops and training tailored to your requirements.
Our expertise spans cross-platform desktop, embedded and 3D application development, using the proven technologies for the job.
When working with KDAB, the first-ever Qt consultancy, you benefit from a deep understanding of Qt internals, that allows us to provide effective solutions, irrespective of the depth or scale of your Qt project.
Qt Services include developing applications, building runtimes, mixing native and web technologies, solving performance issues, and porting problems.
KDAB helps create commercial, scientific or industrial desktop applications from scratch, or update its code or framework to benefit from modern features.
Discover clean, efficient solutions that precisely meet your requirements.
Boost your team's programming skills with in-depth, constantly updated, hands-on training courses delivered by active software engineers who love to teach and share their knowledge.
Our courses cover Modern C++, Qt/QML, Rust, 3D programming, Debugging, Profiling and more.
The collective expertise of KDAB's engineering team is at your disposal to help you choose the software stack for your project or master domain-specific challenges.
Our particular focus is on software technologies you use for cross-platform applications or for embedded devices.
Since 1999, KDAB has been the largest independent Qt consultancy worldwide and today is a Qt Platinum partner. Our experts can help you with any aspect of software development with Qt and QML.
KDAB specializes in Modern C++ development, with a focus on desktop applications, GUI, embedded software, and operating systems.
Our experts are industry-recognized contributors and trainers, leveraging C++'s power and relevance across these domains to deliver high-quality software solutions.
KDAB can guide you incorporating Rust into your project, from as overlapping element to your existing C++ codebase to a complete replacement of your legacy code.
Unique Expertise for Desktop and Embedded Platforms
Whether you are using Linux, Windows, MacOS, Android, iOS or real-time OS, KDAB helps you create performance optimized applications on your preferred platform.
If you are planning to create projects with Slint, a lightweight alternative to standard GUI frameworks especially on low-end hardware, you can rely on the expertise of KDAB being one of the earliest adopters and official service partner of Slint.
KDAB has deep expertise in embedded systems, which coupled with Flutter proficiency, allows us to provide comprehensive support throughout the software development lifecycle.
Our engineers are constantly contributing to the Flutter ecosystem, for example by developing flutter-pi, one of the most used embedders.
KDAB invests significant time in exploring new software technologies to maintain its position as software authority. Benefit from this research and incorporate it eventually into your own project.
Start here to browse infos on the KDAB website(s) and take advantage of useful developer resources like blogs, publications and videos about Qt, C++, Rust, 3D technologies like OpenGL and Vulkan, the KDAB developer tools and more.
The KDAB Youtube channel has become a go-to source for developers looking for high-quality tutorial and information material around software development with Qt/QML, C++, Rust and other technologies.
Click to navigate the all KDAB videos directly on this website.
In over 25 years KDAB has served hundreds of customers from various industries, many of them having become long-term customers who value our unique expertise and dedication.
Learn more about KDAB as a company, understand why we are considered a trusted partner by many and explore project examples in which we have proven to be the right supplier.
The KDAB Group is a globally recognized provider for software consulting, development and training, specializing in embedded devices and complex cross-platform desktop applications.
Read more about the history, the values, the team and the founder of the company.
When working with KDAB you can expect quality software and the desired business outcomes thanks to decades of experience gathered in hundreds of projects of different sizes in various industries.
Have a look at selected examples where KDAB has helped customers to succeed with their projects.
KDAB is committed to developing high-quality and high-performance software, and helping other developers deliver to the same high standards.
We create software with pride to improve your engineering and your business, making your products more resilient and maintainable with better performance.
KDAB has been the first certified Qt consulting and software development company in the world, and continues to deliver quality processes that meet or exceed the highest expectations.
In KDAB we value practical software development experience and skills higher than academic degrees. We strive to ensure equal treatment of all our employees regardless of age, ethnicity, gender, sexual orientation, nationality.
Interested? Read more about working at KDAB and how to apply for a job in software engineering or business administration.
A very common implementation pattern for QObject subclasses is to declare its child QObjects as data members of type "pointer to child." Raise your hand No, keep your hand on your computer input device :-) Nod if you have ever seen code like this (and maybe even written code like this yourself):
A fairly common question regarding this pattern is: "Why are we using (raw) pointers for data members"?
Of course, if we are just accessing an object that we don't own/manage, it makes perfect sense to simply store a pointer to it (so that we're actually able to use the object). A raw pointer is actually the Modern C++™ design here, to deliberately express the lack of ownership.
Ownership Models
The answer becomes slightly more nuanced when MyWidget actually owns the objects in question. In this case, the implementation designs are basically two:
As shown above, use pointers to the owned objects. Qt code would typically still use raw pointers in this case, and rely on the parent/child relationship in order to manage the ownership. In other words, we will use the pointers to access the objects but not to manage their lifetime.
This approach makes developers familiar with Modern C++ paradigms slightly uncomfortable: a bunch of "raw news" in the code, no visible deletes -- eww! Sure, you can replace the raw pointers with smart pointers, if you wish to. But that's a discussion for another blog post.
Another approach is to declare the owned objects as...objects, and not pointers:
This makes it completely clear that the lifetime of those objects is tied to the lifetime of MyWidget.
To Point, or Not to Point?
"So what is the difference between the two approaches?", you may be wondering. That is just another way to ask: "which one is better and should I use in my code?"
The answers to this question involve a lot of interesting aspects, such as:
Using pointers will significantly increase the number of memory allocations: we are going to do one memory allocation per child object. The irony here is that, most of the time, those child objects are, themselves, pimpl'd. Creating a QLineEdit object will, on its own, already allocate memory for its private data; we're compounding that allocation with another one. Eventually, in m_searchField, we'll store a pointer to a heap-allocated..."pointer" (the QLineEdit object, itself, which just contains the pimpl pointer) that points to another heap-allocate private object (QLineEditPrivate). Yikes!
Pointers allow the user to forward declare the pointed-to datatypes in MyWidget's header. This means that users of MyWidget do not necessarily have to include the headers that define QTreeView, QlineEdit, etc. This improves compilation times.
Pointers allow you to establish "grandchildren" and similar, not just direct children. A grandchild is going to be deleted by someone else (its parent), and not directly by our MyWidget instances. If that grandchild is a sub-object of MyWidget (like in the second design), this will mean destroying a sub-object via delete, and that's bad.
Pointers force (or, at least, should force) users to properly parent the allocated objects. This has an impact in a few cases, for instance if one moves the parent across threads. When using full objects as data members, it's important to remember to establish a parent/child relationship by parenting them.
classMyObject:publicQObject{ Q_OBJECT
QTimer m_timer;// timer as sub-objectpublic:MyObject(QObject *parent):QObject(parent),m_timer(this)// remember to do this...!{}};MyObject *obj =new MyObject;obj->moveToThread(anotherThread);// ...or this will likely break
Here the parent/child relationship is not going to be used to manage memory, but only to keep the objects together when moving them between threads (so that the entire subtree is moved by moveToThread).
So, generally speaking, using pointers seems to offer more advantages than disadvantages, and that is why they are so widely employed by developers that use Qt.
Const Correctness
All this is good and everything -- and I've heard it countless times. What does all of this have to do with the title of the post? We're getting there!
A consideration that I almost never hear as an answer to the pointer/sub-object debate is const correctness.
This does not compile; update() is a const method and, inside of it, m_searchField is a const QLineEdit. This means we cannot call setText() (a non-const method) on it.
From a design perspective, this makes perfect sense; in a const method we are not supposed to modify the "visible state" of *this and the "visible state" of QLineEdit logically belongs to the state of the *this object, as it's a sub-object.
However, we have just discussed that the design of using sub-objects isn't common; Qt developers usually employ pointers. Let's rewrite the example:
What do you think happens here? Is the code still broken?
No, this code compiles just fine. We're modifying the contents of m_searchField from within a const method.
Pointer-to-const and Const Pointers
This is not entirely surprising to seasoned C++ developers. In C++ pointers and references are shallow const; a const pointer can point to a non-const object and allow the user to mutate it.
Constness of the pointer and constness of the pointed-to object are two independent qualities:
// Non-const pointer, pointing to non-const objectQLineEdit *p1 =~~~; p1 =new QLineEdit;// OK, can mutate the pointer p1->mutate();// OK, can mutate the pointed-to object// Const pointer, pointing to non-const objectQLineEdit *const p2 =~~~; p2 =new QLineEdit;// ERROR, cannot mutate the pointer p2->mutate();// OK, can mutate the pointed-to// Non-const pointer, pointing to const objectconst QLineEdit *p3 =~~~; p3 =new QLineEdit;// OK, can mutate the pointer p3->mutate();// ERROR, cannot mutate the pointed-to object// Non-const pointer, just like p3, but using East Const (Qt uses West Const)QLineEdit const*p3b =~~~;// Const pointer, pointing to const objectconst QLineEdit *const p4 =~~~; p4 =new QLineEdit;// ERROR, cannot mutate the pointer p4->mutate();// ERROR, cannot mutate the pointed-to object// Const pointer, just like p4, using East ConstQLineEdit const*const p4b =~~~;
This is precisely what is happening in our update() method. In there, *this is const, which means that m_searchField is a const pointer. (In code, this type would be expressed as QLineEdit * const.)
I've always felt mildly annoyed by this situation and have had my share of bugs due to modifications of subobjects from const methods. Sure, most of the time, it was entirely my fault, calling the wrong function on the subobject in the first place. But some of the time, this has made me have "accidental" const functions with visible side-effects (like the update() function above)!
Deep-const Propagation
The bad news is that there isn't a solution for this issue inside the C++ language. The good news is that there is a solution in the "C++ Extensions for Library Fundamentals", a set of (experimental) extensions to the C++ Standard Library. This solution is called std::experimental::propagate_const (cppreference, latest proposal at the time of this writing).
propagate_const acts as a pointer wrapper (wrapping both raw pointers and smart pointers) and will deeply propagate constness. If a propagate_const object is const itself, then the pointed-to object will be const.
This is great, because it means that we can use propagate_const as a data member instead of a raw pointer and ensure that we can't accidentally mutate a child object:
As far as I know, there hasn't been much research about using propagate_const at large in a Qt-based project in order to hold child objects. Recently, I've had the chance to use it in a medium-sized codebase and I want to share my findings with you.
If one already has an existing codebase, one may want to start gradually adopting propagate_const by replacing existing usages of raw pointers. Unfortunately, in a lot of cases, propagate_const isn't simply a drop-in replacement and will cause a number of source breaks.
What I've discovered (at the expense of my own sanity) is that some of these incompatibilities are caused by accidentally using niche C++ features; in order to be compatible with older C++ versions, implementations accidentally introduce quirks, some by compiler bugs.
Here's all the nitty gritty details; feel free to skim over them :)
Broken Conversions to Superclasses
Consider this example:
classMyWidget:publicQWidget{ Q_OBJECT
QLineEdit *m_searchField;public:explicitMyWidget(QWidget *parent =nullptr):QWidget(parent){// ... set up layouts, etc. ... m_searchField =newQLineEdit(this);layout()->addWidget(m_searchField);// this is addWidget(QWidget *)}};
Today, this works just fine. However, this does not compile:
classMyWidget:publicQWidget{ Q_OBJECT
// change to propagate_const ... std::experimental::propagate_const<QLineEdit *> m_searchField;public:explicitMyWidget(QWidget *parent =nullptr):QWidget(parent){// ... set up layouts, etc. ... m_searchField =newQLineEdit(this);layout()->addWidget(m_searchField);// ^^^ ERROR, cannot convert propagate_const to QWidget *}};
C++ developers know the drill: smart pointer classes are normally not a 1:1 replacement of raw pointers. Most smart pointer classes do not implicitly convert to raw pointers, for very good reasons. In situations where raw pointers are expected (for instance, addWidget in the snippet above wants a parameter of type QWidget *), one has to be slightly more verbose, for instance, by calling m_searchField.get().
Here, I was very confused. propagate_const was meant to be a 1:1 replacement. Here are a couple of quotes from the C++ proposal for propagate_const:
The change required to introduce const-propagation to a class is simple and local enough to be enforced during code review and taught to C++ developers in the same way as smart-pointers are taught to ensure exception safety.
operator value*
When T is an object pointer type operator value* exists and allows implicit conversion to a pointer. This avoids using get to access the pointer in contexts where it was unnecesary before addition of the propagate_const wrapper.
In other words, it has always been a design choice to keep source compatibility in cases like the one above! propagate_const<T *>actually has a conversion operator to T *.
So why doesn't the code above work? Here's a reduced testcase:
std::experimental::propagate_const<Derived *> ptr;Derived *d1 = ptr;// Convert precisely to Derived *: OKBase *b1 = ptr;// Convert to a pointer to a base: ERRORBase *b2 =static_cast<Derived *>(ptr);// OKBase *b3 =static_cast<Base *>(ptr);// ERRORBase *b4 = ptr.get();// OK
At first, this almost caused me to ditch the entire effort; Qt uses inheritance very aggressively and we pass pointers to derived classes to functions taking pointers to base classes all the time. If that required sprinkling calls to .get() (or casts) everywhere, it would have been a massive refactoring. This is something that a tool like clazy can automate for you; but that's a topic for another time.
Still, I couldn't find a justification for the behavior shown above. Take a look at this testcase where I implement a skeleton of propagate_const, focusing on the conversion operators:
This still works flawlessly. So what's different with propagate_const as shipped by GCC or Clang?
libstdc++ and libc++ do not use constraints (as in, the C++20 feature) on the conversion operators because they want to make propagate_const also work in earlier C++ versions. In fact, they use the pre-C++20 way -- we have to make overloads available only when certain conditions are satisfied: SFINAE.
The conversion operators in libstdc++ and libc++ are implemented like this:
This specific implementation is broken on all major compilers, which refuse to use the operators for conversions other than to preciselyelement_type * and nothing else.
What's going on? The point is that converting propagate_const to Base * is a user-defined conversion sequence: first we convert propagate_const to a Derived * through the conversion operator. Then, perform a pointer conversion from Derived * to Base *.
If the user-defined conversion is specified by a specialization of a conversion function template, the second standard conversion sequence shall have exact match rank.
That is, we cannot "adjust" the return type of a conversion function template, even if the conversion would be implicit.
The takeaway is: SFINAE on conversion operators is user-hostile.
Restoring the Conversions Towards Superclasses
We can implement a workaround here by deviating a bit from the specification. If we add conversions towards any pointer type that T implicitly converts to, then GCC and Clang (and MSVC) are happy:
template<typenameT>classnon_standard_propagate_const// not the Standard one!{public:using element_type = std::remove_reference_t<decltype(*std::declval<T>())>;// Convert to "any" pointer typetemplate<typenameU, std::enable_if_t<std::is_convertible_v<T, U *>,bool>=true>operator U *();template<typenameU, std::enable_if_t<std::is_convertible_v<const T,const U *>,bool>=true>operatorconst U *()const;};non_standard_propagate_const<Derived *> ptr;Base *b = ptr;// OK
This is by far the simplest solution; it is, however, non-standard.
I've implemented a better solution in KDToolBox by isolating the conversion operators in base classes and applying SFINAE on the base classes instead.
For instance, here's the base class that defines the non-const conversion operator:
Then, propagate_const<T> will inherit from propagate_const_non_const_conversion_operator_base<T> and the non-template operator will be conditionally defined.
The arithmetic operators are not deleted for propagate_const. This means that the implicit conversion operators to raw pointers can (and will) be used, effectively enabling pointer arithmetic -- something that the proposal said it did not want to support!
non_standard_propagate_const (as defined above), instead, does not support pointer arithmetic, as it needs to deduce the pointer type to convert to, and that deduction is not possible.
On a similar note, what should delete ptr; do, if ptr is a propagate_const object? Yes, there is some Qt-based code that simply deletes objects in an explicit way. (Luckily, it's very rare.)
Again GCC and Clang reject this code, but MSVC accepts it:
template<typenameT>classpropagate_const{public:using element_type
= std::remove_reference_t<decltype(*std::declval<T>())>;operator element_type *()requires(std::is_pointer_v<T>|| std::is_convertible_v<T, element_type *>);operatorconst element_type *()constrequires(std::is_pointer_v<T>|| std::is_convertible_v<const T,const element_type *>);};propagate_const<SomeClass *> ptr;delete ptr;// ERROR on GCC, Clang; OK on MSVC
It is not entirely clear to me which compiler is right, here. A delete expression requires us to convert its argument to a pointer type, using what the Standard defines as a contextual implicit conversion. Basically, the compiler needs to search for a conversion operator that can convert a propagate_const to a pointer and find only one of such conversion operators. Yes, there are two available but shouldn't overload resolution select a best one? According to MSVC, yes; according to GCC, no.
Anyways, remember what we've just learned: we are allowed to perform pointer arithmetic! That means that we can "fix" these (rare) usages by deploying a unary operator plus:
In order to support propagate_const on all compilers, and work around the limitations of the upstream implementations explained above, I have reimplemented propagate_const in KDToolBox, KDAB's collection of miscellaneous useful C++ classes and stuff. You can find it here.
I've, of course, also submitted bug reports against libstdc++ and libc++. It was promptly fixed in GCC (GCC 13 will ship with the fix). Always report bugs upstream!
The Results
You can start using propagate_const and similar wrappers in your Qt projects, today. Some source incompatibilities are unfortunately present, but can be mitigated.
An implementation of propagate_const is available in KDToolBox. You can use it while you wait for an upgraded toolchain with the bugs fixed. :-)
C++17 costs more. I cannot emphasize this enough. Not using the latest C++ standards costs more in development time, design, and debugging. While both GCC and MSVC (and upstream Clang) have very good C++20 support, Apple Clang is still lagging behind.
SFINAE on conversion operators is user-hostile. The workarounds are even worse. Use concepts and constraints instead.
Thank you for reading!
About KDAB
Trusted software excellence across embedded and desktop platforms
The KDAB Group is a globally recognized provider for software consulting, development and training, specializing in embedded devices and complex cross-platform desktop applications. In addition to being leading experts in Qt, C++ and 3D technologies for over two decades, KDAB provides deep expertise across the stack, including Linux, Rust and modern UI frameworks. With 100+ employees from 20 countries and offices in Sweden, Germany, USA, France and UK, we serve clients around the world.
Just RiiR🦀 already🙂
Today I learned Substitution failure is not an error.
Giuseppe D’Angelo
Senior Software Engineer
Senior Software Engineer at KDAB. Giuseppe is a long-time contributor to Qt, having used Qt and C++ since 2000, and is an Approver in the Qt Project. His contributions in Qt range from containers and regular expressions to GUI, Widgets, and OpenGL. A free software passionate and UNIX specialist, before joining KDAB, he organized conferences on opensource around Italy. He holds a BSc in Computer Science.
Our hands-on Modern C++ training courses are designed to quickly familiarize newcomers with the language. They also update professional C++ developers on the latest changes in the language and standard library introduced in recent C++ editions.
1 Comment
13 - Feb - 2023
skierpage
Just RiiR🦀 already🙂 Today I learned Substitution failure is not an error.