Understanding the TQT Interface
Understanding the Trinity Qt (TQt) interface layer includes knowing some details about the inner workings of C/C++ and dynamic library symbols. Here are some links to articles providing that foundation:
- Dynamic library structure and purpose: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1418.html
- Symbol resolution and mangling within dynamic libraries on Linux: http://en.wikipedia.org/wiki/Name_mangling#Name_mangling_in_C.2B.2B
- A fair amount about C++ namespaces and their limitations: http://winterdom.com/dev/cpp/nspaces
- Some details of the Qt3 and Qt4 toolkits, in particular how Qt objects are actually defined and exported.
The challenge is straightforward: Using Qt4 and Qt4-based libraries within portions of Trinity. For example, using the Qt4 Webkit widgets. Doing so presents a challenge because Qt3 and Qt4 both use the same class names within the global namespace. For example, QWidget and QObject. That duplicate naming scheme makes compiling difficult, let alone link, a program that tries to use both libraries. Even after getting past the compilation hurdle --- for example, by laboriously isolating the Qt3 code from the Qt4 code in separate source files and creating a neutral interface API between those code portions --- linking would fail or worse, the resultant application would fail immediately on startup. Why?
Provided here is a link to an article about producing symbol collisions: http://stackoverflow.com/questions/2233890/detecting-avoiding-g-symbol-collisions
Witness the conflict using these two commands (execute the commands in different terminals to compare the output):
'nm -D --demangle /usr/lib/libqt-mt.so' 'nm -D --demangle /usr/lib/libQtGui.so'
If one identical text line exists in the output of both commands, the program will fail when linked against both libraries.
A straightforward way to bypass this challenge is to rename all of the publicly exported Qt3 symbols (classes, methods, static functions, static variables) that are in conflict with Qt4. Specifically, those cases where the mangled symbol in the Qt3 library matches (conflicts with) a mangled symbol in one or more of the Qt4 libraries. Namespaces would potentially help, but they have significant limitations that requires manually rewriting large portions of the Qt3 and Trinity source code, in contrast with an almost fully automated renaming process.
Some people have suggested non-portable and messy hacks of manually opening/verifying/loading libraries. That approach is unwieldy. Therefore with renaming QWidget became TQWidget and QObject is now TQObject --- a simple change.
Full Trinity Qt4 Port?
One of the original reasons for the TQt layer was to port Trinity to Qt4. Project members have abandoned that goal. The Qt4 environment is too different in focus and functionality to function well as the base toolkit of Trinity. Nominal testing showed that rewriting a Trinity clone with Qt4 possibly would not be as responsive and snappy as the current Trinity version using Qt3. Primarily because of the different functionality of the Qt4 toolkit.
Integration of certain portions of the Qt4 code has not been abandoned.
Therefore the main benefit of the TQt layer is a straightforward way to permanently resolve the Qt3 and Qt4 symbol collisions whenever any Qt4 integration within Trinity is desired in the future.
Further, fully porting Trinity to Qt4 is easily 5-10 years of solid work with current project manpower. A fundamental project goal for maintaining Trinity is to keep alive the spirit and functionality of the original KDE3 concepts. Porting to Qt4 does not support that goal. Not to mention that Qt4 functionality is different from Qt3 and those differences conflict with how users want Trinity to function.
For the most part, adjusting to this change is easy: just tack a "T" or "t" onto the front of the original Qt3 "Qfoo" or "qfoo" functions and static members.
The challenge arrived because of the way C++, Qt3, and Qt4 function. From a development perspective, Trinity did not need this new TQt layer. Yet to move forward with extending Trinity and to allow integration with newer software technologies revealed that some kind of change was desirable.
Consider the example of KHTML. Replacing the existing KHTML engine with the Webkit engine requires using Qt4 (or writing a new Webkit interface for Qt3, which we lack the manpower for right now). The challenge is linking a Qt4 library into a Trinity library or program due to those symbol conflicts. After "tricking" the compiler and linker, the program will crash at runtime because the C++ runtime will not know which symbols (Qt3 or Qt4) to use when function names and static members have the same name. The TQt layer avoids the mess.
Regardless of how much QT4 integration might be desired for some projects, the original Qt3 layer remains vital for the foundation of much of the Trinity code. Taking advantage of a few new Qt4 features, when appropriate, to add new functionality to Trinity, does not mean the need for the existing foundation of TQt3 can be been magically replaced.
The TQt layer only allows using Qt4 libraries in newly written Trinity code, for specific tasks where the best tool to use is a well-supported library that has been built with Qt4.
The challenge is not as simple as merely relying on the linker to resolve symbol conflicts. The linker creates a reference to a "mangled" symbol (http://en.wikipedia.org/wiki/Name_mangling#Name_mangling_in_C.2B.2B). For an example, consider qWarning. This becomes "_Z8qWarningPKcz" in libqt-mt.so.3.3.8 (nm -D /usr/lib/libqt-mt.so | grep qWarning), and "_Z8qWarningPKcz" in /usr/lib/libQtCore.so (nm -D /usr/lib/libQtCore.so | grep qWarning). If a program references "Z8qWarningPKcz," which library will the symbol resolver choose at runtime? They are not binary compatible with each other, and when the symbol resolver chooses the wrong one at runtime (which it will do quite often) the entire program will crash.
Potential Uses for Qt4 or Qt4-Based Libraries
- Trinity theme engine for Qt4
- Webkit integration
- Multitouch input support
Is there a better way to resolve the symbol conflicts? Please let us know using our Etherpad at http://trinity.etherpad.trinitydesktop.org/46.