 Welcome, Kai, and thank you again for being here. Thanks for having me. It's a great pleasure to be once again on an Academy. It's been a while. So, yeah, I want to talk about parting user applications to Qt 6. So, the focus is a bit on user applications. You heard yesterday, if you have been joining yesterday an update about KD Frameworks and that they are also working on this and so on. But this is not, you know, to talk in that sense isn't KD specific because, to be honest, there would be others who would be probably much more into the current state of things. But kind of what it takes in general to port a Qt 5 traditional application to Qt 6. All right, I hope you can only hear me fine. If not, the screen. So, yeah, when preparing the slides, actually on Friday, a colleague kind of from completely different presentation talked to me and said, you know, all the technical presentations often missed the why. So, I was adding this slide on last second. So, why Qt 6? Why porting to Qt 6? And, to be honest, Qt 6 is in a lot of way just a logical kind of conclusion or going forward from Qt 5. So, from times to times inside Qt, we just decide, okay, you know, there is enough architectures to be changed that we don't really want to do in minor changes, minor patch sets. There is enough, you know, deprecated API now that we want to do a clear cut and actually get rid of deprecated API and, you know, also do, yeah, just architectural stuff like updating the C++ version and so on. And then it's kind of the discussion states and then we say at one point, okay, that should be actually and that's my major version. So, in that sense, it's like logical conclusion from Qt 5. On a very high level view, what we want to aim with Qt 6, what you can expect also in the next years to come is, well, I mean, word is changing. We are looking to new platforms. Also architectures like the whole Mac OS and ARM thing is going on. The other side of the coin is graphics. There is a lot of development there where for instance, I don't know, with Qt 5, we did a bit of a bet on OpenGL and the whole Qt Quick story was built on top of OpenGL. Now we have completely different landscape where there is Vulkan and Metal and probably more direct X coming and evolving. So that also requires some bigger changes inside Qt. Qt Quick is an ongoing thing since years. Also since Qt 5 released, we have been learning a lot how to also write bigger applications with Qt Quick. The challenges that comes with that, with the current Qt Quick integration in terms of tooling, in terms of maintaining bigger applications. So there is a lot of going on there to make this better. And yeah, on the strictly C++ side, there's been discussions about whether our current containers are still good enough, how to want to evolve them, that we have some API that we don't want to advocate anymore or let people use anymore and so on. So that's all good reasons for Qt 6. If you are interested in particularly that, like the bigger picture, I can only refer you maybe to the talk by Lars Knoll next Wednesday, I believe. You will be going into details about this. So I want to kind of focus more on a very practical, okay, let's get it done. Let's take an application and port it aspect than the bigger picture, fair enough. So the next couple of minutes, we will quickly have a look at the current state of Qt 6, then how, what it takes in terms of build system in CMake integration to port an application to from Qt 5 to Qt 6. And also the C++ side was already hinting at containers and so on. What does that mean for me, for my application and tooling available to help you with the transition. So yeah, Qt 6.0, that released last December, which I was personally quite happy about because it was actually almost or very near the original schedule date. If you look big into Qt history, that wasn't always the case. So I think we did a good job there. Early on Qt 6, though, we already decided that we kind of can't cope with porting all of the Qt 5 modules to Qt 6 at the same time and our Qt has been growing over the years. And it's just from the timing and quality and effort perspective, very challenging to kind of tackle all these modules with the existing people working on it. So we said with Qt 6.0, we want to kind of port the most important modules, the modules we consider most important and then quickly follow on in minor versions to also port more. So that's what you see here, Qt 6.0. There was already quite some modules in Qt 6.1 that's released in May. We did do a couple of minor ports and another big chunk will happen with 6.2. And there is this saying now that and you often read people that saying, learn our 6.0 is for this reason alone, just technical preview and you can't use it and so on. I don't agree. I think the quality of 6.0 and 6.1 is actually, is there's always things to improve. And I'm not saying there is any kind, not any great questions, but also looking between history, I think it's actually fine. We're doing, have been doing a good job there. The community has been doing a good job there. And yeah, but obviously if you depend on Qt multimedia, I think was raised yesterday in the chat and the discussions, then you might need to wait until 6.2, which is currently scheduled for September. So the very first thing you might want to just check out is kind of the list of dependencies you have and check what the state is there. So with that in mind, let's move to the build system. So very practically, I assume you're using CMake, you're having an application that's working fine with Qt-5, what do you have to do to port it to Qt-6? So yeah, I mean, this is typical API that you will see in Qt-5 with Qt applications building against Qt-5. So CMake, you start usually with a find package and actually saying what actually I want, like in this case, it's Qt-5 with Qt-Rigids. Then you have APIs like where you directly might want to call functions that are provided by the Qt modules. They usually start with the Qt-5 underscore. And in the end, you're also using the targets that are imported by the find package to, for instance, link against Qt-Rigids. And again, you have a big Qt-5 there. So you can already see that if you compare it, for instance, C++ API, the CMake API is very versioned, which is not a big problem for a very straightforward port because you can just go on and must replace Qt-6 with Qt-5, so a Qt-5 with Qt-6, maybe in a separate branch and suddenly a golden. And that's really how it works, right? So we haven't changing much in the names of modules and so on. There might be some smaller things, but overall there isn't that much changes in the CMake API compared from Qt-5 to Qt-6. So most of the things will just work. Anyhow, this has a problem in that you are now having two different versions. And unless you really want to kind of, I don't know, do a one-off port or so, you often have the requirement that you want to support Qt-5 and Qt-6 at least for some time by the same code base. So what do you do there? We had problems ourselves. So we're already in 5.15, we have been looking into this and added some new API. So that's a very canonical solution if you want to support only Qt-5.15 and future Qt-6 versions. So what has changed here? First of all, you might notice that there is two different find package calls. What's that? So the first call is actually using the names argument from find package, which is, I don't think that often used, but the idea here is that I tell CMake to look first for Qt-6 package. And if that fails, and for instance, widgets are not found, then look for Qt-5 package definition. And CMake has all this kind of a bit weird ways to locate packages. So that sounds exactly like you want, right? We want to say first Qt-6 and then Qt-5 and then work with that and store that into a package called Qt in this case. That should be enough, but to be honest, it doesn't work out of the box because for instance, even from the auto RCC inside or auto mock was it, I think, inside CMake support doesn't work with that. So it expects that the package is called Qt-6 or Qt-5. So what to do there is basically just using that first call to basically determine the major version that's stored into a CMake variable called Qt-Version-Major and then use that argument to actually load the real thing so that we are having again a like defined package Qt-5 or Qt-6 here. So that's straightforward. Another change you see here is that we are have been actually calling something and function here that is the CMake function that's defined by Qt without a major version. So not Qt-5 underscore, but just Qt underscore. So that's an alternative API we've been adding in Qt-515 for all the functions that are exposed by the Qt modules to kind of help in the porting. And that's actually also two for targets. So you can also just refer to Qt underscore underscore widgets and it doesn't matter with its target imported by Qt-515 or Qt-6. So yeah, the big thing to remember here that that's API that we only added for 515. So if you want to support even older Qt-Versions Qt-514 then it looks a bit more complicated but still manageable. So in this case, you can't rely on the unified function names. So you know, you have to basically always do the checking on my Qt-5 or Qt-6 and also the targets. In this case, you can just use the Qt-Version major variable as a target. What else to remember? Well, I already hinted that Qt-6 is requiring C++17. So that might be a good idea to check whether you are also requiring that or maybe you're already requiring a new version. I don't know, but yeah, it's a good check to do that you might also want to bump your C++ standard requirements. And the last thing about CMake that I want to mention is that there is a new module called Core 5 Compat. And the name says it all. It's a class, sorry, a library module which contains some of the API that we removed from Qt-6 Core specifically. So for any non-trivial port, there's a good chance that you actually want to include that thing to obviously only if you compile against Qt-6. But we will come to examples about this in a second. So the summary goes, yeah, my recommendation is for commands that you want to use from Qt. If possible, just use the unversioned ones. I think we should go on with them and actually use make them default. So then, for instance, the Qt-6 to Qt-7 port won't have that problem anymore. The alternative also for targets is to use just, you know, check or embed Qt version major variables so that you can support both Qt-5 to Qt-6 and the C++ 17 requirement that I mentioned. To be honest, I'm like, I don't hear anything. I'm not even sure whether people listen. So maybe that's a good point where if there's already questions, all good. Okay, good. Then I will just go on. So yeah, so this is about basically assuming that you're having a more traditional application and you want to port it then doing the build system change and then you most likely will run into still some compilation issues. So that's somewhat to be expected, but obviously what exactly it is depends largely on the application. So what we have is a testbed, what we can offer as a case study is actually QtGrader. So QtGrader is our IDE. It's actually a large application. It uses a lot of the modules in Qt, though, I mean, obviously it's like, you know, for instance, Qt multimedia I think is not used. So there is exceptions. It's a certain type of applications you have that problem always with case studies. Anyhow, it's I think a good idea to start looking into that. So what we did is all the quite early in the Qt6 development to try to make the current creator code-based compile also with Qt6 so that we can learn from it. Also we provided feedback so that, for instance, some of the changes weren't done or were reverted and so on. So that's kind of also the role of QtGrader to be a bit like the test case there if changes are acceptable or not. So I set the same code base, so it was not kind of a one-off branch or anything like that, but we always want to support multiple major, sorry, multiple Qt versions with QtGrader and Qt6 is not an exception. So in that sense, it went on a bit in parallel with the normal development of QtGrader. What we did though very early on is kind of trying to collect all the changes that are somewhat related to the Qt6 port into one item in the bug tracker. So if you want, you can look it up. We're talking about roughly 180 patches there, which sounds a lot, but on the other side, most of them are also very small and so on. So it was like, there was no big, I mean, I convert everything to Qt6 commit, but rather module by module and case by case or in a lot of cases. So what I did as preparation of this talk, I was actually kind of going through the list and trying to categorize the things a bit more to see actually whether there is patterns and some of the things was obvious, but some of them things even surprised me a bit so that the result of this is on the right side. So yeah, I was basically going through the patches and saying, okay, this one for solely for fixing Qlink list, for instance, this one was affecting also, I don't know, some other use case, this one was for regular expressions or records. So what you can see here is a bit of a pattern that yeah, there is like the largest bar that's Qrexp, I will, for the most important parts I will have follow-up slides. So we will look into this, Qstring, Qcharev is another case, which might be interesting. Then there is a whole set of things around containers. So let's also have a look at that. And the second largest one, I don't know, my other is obviously a bit of a basket for everything, but replace API deprecate in Qt5. That was actually something that I can kind of recommend in general. So if you want to part your application to Qt6, the best thing probably to do is first port it to Qt515 if you are not building with Qt515 yet and actually deprecate, sorry, fix all the cases where you have API and use API that's already deprecated in Qt515. So if you haven't done that already, there is a nice define called QtdisableDeprecatedBefore and in this case, that's hex for 5F, that's 550. So it says Qt, remove all or rather pre-compiler, remove all the declarations of API that is deprecated in Qt515, and then you will get compilers if you're using them still. So I can only recommend that because that catches already a lot of cases. So, but let's look into some more interesting topics probably than just smaller deprecations because a lot of them are really straightforward. And the documentation usually just tells you what to use instead. So QwackExp, that was the largest bar, if you remember. So what about that? QwackExp is the way to handle regular expressions from Qt4 to Qt5 times, basically. So, yeah, the class is heavily used in Qt Creator which is, might be a bit, IDEs are all about text manipulation and so on. So that might not be a complete surprise. And we've been using QwackExp heavily in hundreds of places, but already in 50, there was a replacement for QwackExp. So Qwack, a regular expression was introduced back then. So, yeah, but we never managed to do the port, basically we had cases where we had QwackExp, but we had also lots still of cases where we had QwackExp. One of the reasons might be, well, it was just working, all right, fix code that just works. The other one is that the porting is, it's not just a single, you know, text per place. So that's a bit like, if you know the architecture of QwackExp a bit, that is both representation of the regular expression, but also, for instance, a representation of the results. So it was a bit of a mixture there. And with QwackExp, we've been splitting up into two classes, QwackExp and QwackExp match. That's one of the examples, and there is a few more that we got. One thing there is kind of a bit of a gotcha, that's why I'm mentioning it, is that, for instance, also the wildcard syntax is not exactly the same. So QwackExp uses a different backend for that. And in general, it has more features, it has more features, more advanced features, handfuls of Unicode and whatnot, but it's also a bit stricter. So you might have a regular expression that is working as a string, you pass it to WackExp and it's not working anymore with QwackExp. That's not very often. I mean, the common set of regular expressions are the same, but if you're using some features and relying on some more obscure details, then you might hit that. The problem with that is your compiler won't find it, right? It's just a string to the compiler, so you don't get a nice compiler warning, you get just different behavior or crashes even, depending on the result of this. So that's a bit of a gotcha. Remember, maybe checking the regular expression and maybe putting a bit more effort into checking that it still works as expected. That's also true for another case, which is kind of using not actually per regular expression, but wildcards like using from the shell. So also there, okay, QwackExp had directly supported this. QwackExp doesn't, but it has a static function which you can call to convert a wildcard expression into a regular expression. That works also, but there is also some smaller details to remember, for instance, when it comes to slashes in the string and so on. These are all, by the way, very nicely documented in the porting guide. So if you want to know details about this one, I would actually recommend going to the porting guide. I just want to mention another kind of difference, slight difference, which is a bit annoying. It's like that you had QwackExp matched length and okay, I just see an error. It's actually QwackExp, captured length. And these sound like almost identical, but the return value, for instance, for the case where there was no captures is different. So in the one case, it returned minus one. In the other one, it returned zero. That's an example. It's a bit unfortunate if you kind of just mass replace things and suddenly things break, don't break anymore. Sorry, don't work anymore. So again, I have to say, we had lots of cases of QwackExp in Qt5, sorry, Qt Creator and most of them are really straightforward to port. I could open now with the browser, but I'm not exactly sure how this works here in this chat. So if you're interested, we can also look at that later. That should be it for QwackExp. So remember a bit to take special care there that actually it works the same as before. Another of these cases is QstringRef. So what is QstringRef? It is an optimization that you can do already in Qt5 times, maybe even Qt4 times. I don't remember where you want to have a look at, you want to analyze a string, a Qstring, and you want to kind of have parts of it, for instance, for further processing, but you'd want to write the overhead to kind of copy and paste that because Qstring will then allocate a completely new object and so on, that might be expensive. So QstringRef is an optimization for that, which is available in certain classes and of Qt and is exposed there. And you could use it to work with substrings basically of underlying strings. So being again an IDE Qt Creator was also making use heavy use of that. What happened now is that in Qt5 10 already, so already like a couple of versions ago, there was a proposal for an alternative API which is considered superior, that goes under the name QstringView. The very same concept, bit of a different implementation and also much more generalized. So QstringRef was really working for Qstrings. QstringView you can also use for, I don't know, standard 16 strings and so on. So it's a bit decoupled from Qstring implementation. And it's also now available in lots of other API like in Qt Core where QstringRef was kind of limited in terms of Qt API that's exposed there. If there's also, for instance, now also a QbyteArrayView and so on. So the concept is a bit more generalized. So in general, it's recommended to use that and that's the reason why we removed QstringRef from Qt Core. It moved to Qt5 Compat. But the problem is that again, the API you're not using often QstringRef alone, but you're using it with some API that exposes it. So in Qt and these API is also can. So it's time to port. That's at least what we did in Qt Creator. I'm small, gotcha there. Again, not that common, but there is a bit of a different behavior when you're using have you on a string and then change the string underneath. So might be obvious that that's not a good idea to do. And the problem, the thing is that QstringRef was trying to fix a couple of cases there while QstringView does not. So again, we had one case, I think in Qt Creator where this lead to a crash. Just to let you know. On with containers. So something that's a bit annoying, I have to say is Qhash versus QmultiHash. So a Qhash, right? You know the structure, it's a key value thing and the container is optimized for looking up value by key. And then there is also the idea to have that also as a multi-hash, which means that there is one key, but there might be multiple values. So in Qt5, that was a bit of a weird setup because there was QmultiHash, but actually the implementation was in Qhash and you could actually use Qhash as a, so QmultiHash was derived from Qhash with convenience functions, but the implementation was all the logic was in Qhash and you could use a Qhash as a multi-hash by just using explicit API like insert multi. So it was a bit of a mixed of concepts and in Qt6 it was decided to split it up and have two template classes Qhash and QmultiHash, which I think makes complete sense, but if you're porting, you might have cases where you have having a Qhash in your API and you actually have to decide was it meant to be a multi-hash or not. So in that way, it's now a more explicit, but that's something that you can't kind of automatically decide, obviously if you have a header file somewhere and it takes a Qhash, then you have to decide whether it should be or not a QmultiHash. So that's one of the things where, which you can't just automate by search replace. The other thing again is like, and I think that's not a common pattern if you see that, that also the implementation of Qhash and QmultiHash changed a bit from a node-based approach to a two-stage lookup table. So interesting topics, if you're into that, the bet-in line is obviously it should be faster now, but it also has some implications in terms of reference stability. So if you have a hash and you take a reference or a pointer to, for instance, a value, and then you change the hash, then that will, well, I have to say question more often. So I mean, even in Qt five times that was a bit problematic, but it was usually working and you know, you shouldn't rely on that, but you know, that's how it is. You write code and it works and you keep it. So we had one case there or two cases, I think, where we had to adapt our implementation and the solution was to actually go to standard unordered set in this case because standard unordered set has this explicit guarantee that you can have a reference and it will only be invalid if the item itself is deleted or the value itself or the key value part pair itself is deleted. So that might be, you know, an option to use. Now, if you're into containers and have been following the discussion, I don't know, just a bit then Qlist might have come and Qvector might have come to your attention. So the problem with Qlist was twofold or is twofold. The first one is that it had a very interesting implementation in that try to be clever whether the elements of a list would be allocated continuously in the memory or whether the memory allocated for Qlist is just an indirection and points to somewhere else in memory. And Qlist had some try to be clever and determine what you want and what's best for your use case. Turns out that very early on, people pointed out that this is, you know, not always work for creating the right results and that we should rather use Qvector which was added as a very explicit way to say, okay, it's always one continuous block of memory and the elements are in there which is faster in terms of memory access. So if you wait to rate over things, then that's usually the preferred thing. All right, so then there was discussion on what to do in Q6. That was one problem. So we had memory model. And the other problem was that there is the STD library which unfortunately has different names or the same names for different concepts. So they also have a list, but it was actually, they have a list and a vector and Qvector is standard vector but Qlist wasn't, you know, standard list but was something in between. So there was quite some discussion on how to solve this. One of the ideas was to, I don't know, deprecate Qlist and just live with Qvector. The thing is Qlist is everywhere. It's in every, it's a lot of, I mean, there is hardly, well, I'm not exaggerating, but it's arguably the most used class of Qt maybe in line with QString. So changing such a fundamental thing and deprecating it would be really kind of the mere definition of a source compatibility break. So we decide not to do that in the end but instead adapt the implementation of Qlist to be more or less that was Qvector was previously. So yeah, that's what happened in Qt6. So if you have still fights with your colleagues about whether to use Qlist or Qvector, the good news is you can stop now because you can say in Qt6, it's all the same. Qvector is now a type def Qlist. And yeah, you might imagine that's quite a fundamental change. To be honest to my surprise in Qt Creator, we barely noticed. So there might be performance improvement in some cases, performance degradation in other corner cases, but to be honest, kind of in the overall thing really can show up too much. So that port was actually quite forward except that if you are really into pre-declaring everything like in the headers for optimizing compile times, then you have a problem because Qvector has been changing. So it's not its own class anymore. So yeah, another word about reference instability, might notice the theme now Qlist for bigger, for certain types that were large enough and actually, you could take a reference and to an element. And because it was at a different memory location you could change the Qlist and the reference was still valid, not true anymore with Qlist and Q6. So if you are, if you're having a case where you, I don't know, have that problem or anyhow, you are disagree that actually the new implementation is better than the cop out is to go to standard list because that is always this structure where you have the elements are stored separately in memory. And if you're using Qlink list, that was also one where we noticed, okay, Qlink list is actually a standard list from their concept here. It's rarely used at least in QGrade or in Qt. So we just decided, okay, let's move it to Qt5 Compat. Again, you can find it there, but to port it actually, just use standard list. Good. Yeah, the summary is that porting is mostly straightforward. Yeah, I mean, I'm, it might not me appear from the slides that you showed, but I was kind of after showing you the interesting things. So most of the patches of the 180, whatever patches that I had was actually quite straightforward. So don't get confused by these foot nodes basically that I'm bringing up here. And yeah, the recommendation is port to Qt5 15, fixed applications, then moving to Qt6 should be actually quite straightforward. But what exactly you're affected with is obviously depends on your, much on your application. So we were looking to use a feedback, for instance, and there it's mostly Qhash. I mean, it's also just a couple of things, but sorry, there was Qvariant. So Qvariant was got a lot more stricter and so on. So what we really try to do, and all I'm telling you is probably documented in the documentation. So there is a porting guide on doc.io that you can go to. And there's also separate pages for every single module like Qt Core, like Qt Qui, and so on, where the things are kind of explained. And we try to be really thorough with that too. Yeah, to help you there. So it's a good idea to either directly read that thing or at least bookmark it if you are actually doing the porting. And yeah, we also much appreciate feedback about this one, to be honest, because I think now is the time to really cover all those things and so on that people can benefit from it. So yeah, the last thing I want to mention is that I've been saying a lot of times that it was a lot of mechanical things. So the obvious question is, can't we automate this? And we have the same idea and actually worked together with KADEP and the KDE community on Clazy. So if you haven't used Clazy yet, it's I think originally created by KADEP, a tool that is built around clang and allows like these code transformations. So code checking and code transformations. And what the latest version, which is I believe 1.9 features now at least these four fixes. So these are not, so Clazy had a lot of fixes and checks but they split up a bit into, this is really kind of, everybody should use that. So it's in default set of things to be checked and then there is mine of manual fixes. So that's the category of manual fixes. So if you just run Clazy without any arguments you don't get them. But if you're into starting a port, then be aware that there's Clazy checks for header fixes when header changed forward fixes. That was actually also quite often that you, I don't know, some header was included by some other header which is not the true anymore. Q hash signature is not the one size T versus int and so on. So I can only recommend to use that. And we'd like to have feedback actually about this because to be honest, we didn't receive so much about this. So that means either people are perfectly happy with it or with a doubt or they haven't discovered it yet. So talk to us, tell us what you think about this one. I think that's it. Hey, thanks so much. Well, we have reached time unfortunately, but you do have a list of questions. I'd love to invite you to the Matrix channel to answer some of those questions in chat if you have some time. Sure. Yeah, I'll be there. Great, well, thank you. I will connect with you also off screen to make sure you're able to get all of the list of questions too. So just ping me and we'll get you all set. Cool. Thanks a lot and have fun. Thanks, thanks so much. Have a great rest of the day. You too.