Can an application depend on multiple versions of Qt? One directly and the other through a 3rd party library.
-
I'm writing a C++ library whose only third party dependency is Qt. This version of Qt was built with non-default compiler flags like -qt-sql-sqlite. From here on out I'll refer to this library as MY_LIBRARY.
Several client applications need to use MY_LIBRARY as a third party dependency. However, each client application already has their own version of Qt as a third party dependency. Some client applications depend on a very old version of Qt but some depend the latest version of Qt. Some client applications depend on a default build of Qt and some used non-default compiler flags. Each client application would like to add MY_LIBRARY as a dependency without having to change their software or the version/build of Qt that they already depend on.
Obviously, when I package MY_LIBRARY to be used as a third party I'll have to include the binaries/headers of the Qt. I fully expect those binaries/headers to conflict with the Qt the client application is already using. Is there a way to build/package MY_LIBRARY such that a conflict doesn't exist?
...I've been experimenting with building MY_LIBRARY's Qt with the -qtnamespace compiler flag but it doesn't seem to be solving the problem.
-
This depends a bit on the OS you use and the dll names.
In general, I would answer this question with no, it will not work. This is due to a dll with one namecan only be loaded once. And Qt relies on some initialisations, like the global QCoreApplication object.
But in some cases, it might. If the different dlls have different names and they are all strictly in namespaces (including internal global functions of Qt) it might work. But in your custom version, you may not use anything that depends on global objects like QApplication, QCoreApplication etc. This would make that very hard I think, especially if you have Qt in your Dll interface.
-
bq. This depends a bit on the OS you use and the dll names.
We support 64 bit Linux and 32 bit Windows. I assumed that if MY_LIBRARY's Qt dlls had the same names as a Client Application's Qt dlls then it might not work (as you've mentioned). In an attempt to get around this when I built Qt for MY_LIBRARY I did so using the -qtlibinfix configure option.
bq. But in some cases, it might. If the different dlls have different names and they are all strictly in namespaces (including internal global functions of Qt) it might work.
When I compiled Qt for MY_LIBRARY I used the -qtnamespace compiler option so I think that "they are all strictly in namespaces"
bq. in your custom version, you may not use anything that depends on global objects like QApplication, QCoreApplication etc. This would make that very hard I think, especially if you have Qt in your Dll interface.
I'm not sure what makes a Qt class global. However, MY_CLIENT is only using the following Qt classes...QXmlStreamReader, QXmlStreamWriter, QFile, QFileInfo, QFileInfoList, QDir, QIODevice, QString, and QVariant. In the future I intend to use some of the class in the QtSql module like QSqlDatabase, QSqlQuery, etc.
If by having Qt in my DLL interface you mean that the MY_CLIENT's header have Qt #includes, then yes I do have Qt in my DLL interface.
Are there any other questions I can answer that would help you determine if this is possible or not? Thanks for your help!
-
bq. If you use QtClasses in the dll interface, then you can’t mix the version!
Thanks for the help.
Just out of curiosity can you tell me what use case Qt was attempting to solve by implementing the "qtnamespace" and "qtlibinfix" compiler options?
I assumed one reason "qtnamespace" was implemented was so MY_LIBRARY could reference Qt classes with a specific namespace, for instance "MyCustomNamespace::QString". Meanwhile the CLIENT_APPLICATION could reference Qt class as they normally would, for instance "QString". Thereby removing any ambiguity over which Qt each is referencing because the namespace (or lack thereof) would explicitly specify which Qt the reference was intended for.
I assumed one reason "qtlibinfix" was implemented was so MY_LIBRARY could link against a special version of Qt libraries with a specific name, for instance "QtCoreMyCustomNamespace.so". Meanwhile the CLIENT_APPLICATION could link against Qt libraries with a normal name, for instance "QtCore.so". Thereby removing any ambiguity over which library each piece of code is linking against and removing the possibility of one library not being loaded into memory because another with the same name has already been loaded.
Thanks again for the help. It looks like I'll have to give up on this approach but I really would love to understand those two compiler options because it seems as though I've seriously misunderstood their purpose.
-
Perhaps (I never tried it) it works with the name spaces. But what should the client do with the MyNS::Qstring in the interface? How should he use it in his "standard" Qt Application? Always transform via utf16? How top copy such MyNS::QVectorMyNS::QVariant into something useful?
From my POV, it is useful if you use Qt internally but not in the interface of the class...
-
Perhaps I misspoke. What I should have said is that MY_LIBRARY's headers have #includes for the specially built version of Qt. However, those #includes are needed for data members not return values or method parameters. The CLIENT_APPLICATION will never exchange Qt objects with MY_LIBRARY...instead primitives, std::string, stl objects, etc are exchanged between the CLIENT_APPLICATION and MY_LIBRARY.
Perhaps this approach is viable?
-
That could work.
I'm not 100 % sure, if there might be some internal global functions / objects inside Qt that are not in the namespace, then that could make problems in linux, as each global name exists only once inside a process.If that is not the case, it could work.
-
You can build your Qt version with namespace to work around the issue of duplicate symbols: All "your" Qt symbols are in YOUR_NAMESPACE, the 3rd party Qt is most likely populating the global namespace with their symbols.
That is rather error prone though and I would avoid this.
-
I just wanted to come back and post the solution to this problem that ultimately worked for me...
I compiled Qt with special compiler options. The "qtlibinfix" option forced the Qt libraries to be given a custom name, for instance "QtCoreMyQtNameSpace.so". The “qtnamespace” option forced the Qt classes into a namespace of my choice.
I use CMake to build MY_LIBRARY and it required me to jump through a few hoops to link against the custom build of Qt (thanks to the non-default name space and non-default library names). I can't remember everything I did but I know I had to use this command...set(QT_LIBINFIX "myQtNameSpace"). Ultimately I was able to build MY_LIBRARY and everything seemed great.
However, when I packaged MY_LIBRARY as a third party for use in CLIENT_APPLICATION I noticed MY_LIBRARY's Qt headers used a macro for the namespace (as opposed to explicitly setting the namespace from the qtnamespace compiler option) AND the Qt headers that CLIENT_APPLICATION already uses had the same macro. I couldn't figure out a way for CLIENT_APPLICATION to have that macro evaluate to the default namespace for it's Qt headers but evaluate to my custom namespace for the Qt headers MY_LIBRARY uses.
Ultimately I rewrote MY_LIBRARY so it's headers never #include a Qt header. I used forward declarations when needed and I had to write a wrapper or two around Qt objects. This allowed me to package up MY_LIBRARY without the Qt headers, just the Qt binaries. Once that was done CLIENT_APPLICATION was able to use it's own version of Qt and MY_LIBRARY at the same time. Mission accomplished.
I never really understood the possible issues you guys mentioned with the global namespace. It has been over a month now and everything seems to work fine.
-
If everything is packaged fine into a namespace, you have no objects inside the global namespace and all is fine. I was just not 100% sure, whether it is that way internally in qt.
Otherwise, on Linux, you could run into problems, if you have different global objects with the same name. As it works, it seams to not use global objects now outside the namespace.