Bogus errors together with their messages can have a large number of reasons – full hard drives being one of the classics. When it comes to programming and especially C++, the possibilities for cryptic, meaningless and misleading error message are infinite.
A nice one bit us at one of our customers the other day. The message was something like
QLayout can only have instances of QWidget as parent
and it appeared as standard error output during program start-up. Needless to say that the whole thing crashed with a segmentation fault after that. The only change that was made was a header file that was added to the Qt files list in the CMakeLists.txt file. The Qt class in this header file was just in its beginnings and had not yet any QLayouts, or QWidgets in it. Even the C++ standard measure of cleaning and recompiling everything didn’t help.
So how is it possible that an additional Qt header file that has not references to QLayout and QWidget can cause such an error message?
As all of you experienced C/C++ developers know, for the compiler, a code file is not only the stuff that it contains directly but also what is #included! The offending header file included a generated ui description file which you get when you design your windows – or Forms in Qt terminology – with the Qt designer and use the Compile-Time-Form-Processing-approach to incorporate the form into the code base.
But how can that effect anything?
The Qt designer saves the forms into .ui files. From that, the so-called User Interface Compiler (uic) generates a header file containing a C++ class together with inlined code that creates the form. Form components like line edits, or push buttons are generated as instance attributes. The name of the class is generated from the name of the form. You can even use namespaces. By naming it e.g. myproject::BestFormEverDesigned the generated class is named BestFormEverDesigned is put into namespace myproject.
So far, so nice, handy and easy to use.
When you create a new form in qt designer, the default name is Form. Maybe you can guess already where this leads to…
Two forms for which the respective developers forgot to set a proper name, existed in the same sub project and had been compiled and linked into the same shared library. The compiler has no chance to detect this, because it sees only one
class Form
{
at a time. The linker happily links all of this together since it thinks that all Forms are created equal. And then at run-time … Boom!
I will have to look into a little Jenkins helper which breaks the build when a Form form is checked in…