I spent quite some time today trying to figure out why kcm modules don’t load properly – this is the reason I have been bemoaning the lack of focus follows mouse all this time, because the kwinfocus module doesn’t show up. Lots of kdelibs compiles later with extra debugging and tracing added to KPluginFactory, I have come to the conclusion that it’s actually a compiler bug. This is the first time in a few months that I think I’ve hit a real bug. Or at least, something that is so different in gcc and in Sun CC that it’s annoying to work around and silently displays different runtime behavior. These things are to be expected because KDE is pretty demanding C++ code.

Here is some code that exercises the bug. It’s derived from KPluginFactory.

#include <iostream>

class object { } ;
class A : public object { public: A(int i) { std::cerr << "A::A(" << i << ")\n"; } } ;
class B : public object { public: B(int i) { std::cerr << "B::B(" << i << ")\n"; } } ;
class E : public A { public: E(int i) : A(i+1) { std::cerr << "E::E(" << i << ")\n"; } } ;

What this bit of code does is just set up an inheritance hierarchy. We’ve got some classes that log their constructor calls. This is like we have QObject, QWidget and KParts (which are the relevant classes for KPluginFactory). Next, we need to have some kind of factory methods.

typedef object&nbsp;*(&nbsp;*CreateFunction)(int);

template<class impl> static object&nbsp;*aFunction(int i) { return new impl(i); }
template<class impl> static object&nbsp;*bFunction(int i) { return new impl(i+17); }

template<class impl>struct Creator {
CreateFunction function(A&nbsp;*) { return &amp;aFunction<impl>; }
CreateFunction function(B&nbsp;*) { return &amp;bFunction<impl>; }
} ;

This part is the factory methods themselves. The idea is that you can call function() and it will invoke the constructor for the type. Finally, we have the method that calls the relevant creator (this is analogue to registerPlugin).

template<class T>
void foo(int i,
&nbsp;&nbsp;CreateFunction f = Creator<T>().function(reinterpret_cast<T*>(0)))
{ f(i); }</blockquote></tt>And a main, to exercise this code:<blockquote><tt>int main()
{ foo<A>(0); foo<B>(1); foo<E>(2); return 0; }

(You can get this code from BionicMutton for now; there’s some minor modifications for layout, license and clarity) Now, the idea is that the three calls to foo() all end up in the respective class constructor; I’d expect output like A::A(0) B::B(18) A::A(3) E::E(2) where the second call to the A constructor is because it’s the base class for E. And if I apply g++ 3.4.6 to the code, this is what I get. So let’s turn to the code that Sun CC (Studio 12, CC: Sun C++ 5.9 SunOS_i386 Patch 124864-01 2007/07/25) produces. There’s three invocations of the constructor A, with values 1, 0, 2. Clearly very very different from what gcc is producing: I’m asking for a B, but getting an A! The different instantiations of the factory template methods are getting lost.

I stared at this for a long time. Then I read manpages; there’s a C++ compiler option tmplrefstatic which seemed to be in the right ballpark, or at least playing the same game, but that doesn’t have any effect on this behavior. On a whim, though, I changed the return type of foo(). I guessed that the function signature was insufficiently different between the instantiations of foo (all void(int)) to prod the compiler into doing separate template method instatiations. I replaced void with T * and added a bogus return (T*)0; in the method. That’s not a heavy extra burden.

Much to my surprise, this little hack just worked. The right factory methods end up being called, and with Sun CC on both amd64 and SPARC I get the expected – that is, gcc – output. I patched KPluginFactory with the same hack and that, too, comes to life. I can finally find focus follows mouse in the KWin config module – because for the first time, it’s actually there.

Now that I’ve written this up as a compiler bug, I get to wondering what the required behavior is for C++. Is gcc being generous by generating distinct instantiations where none are required (this strikes me as extremely unlikely in this case, but gcc is often generous in what it will accept)? Or is it really a Sun CC bug (in which case Roman has an extra war story to add to his talk on the ways in which KDE has shaken out bugs in his compiler). Folk who can quote chapter and verse of the C++ standard at me are welcome to point it out (but if you do, I will bother you with some other obscure cases in KDE).

The Wayback Machine ⏲ does not archive everything. Broken links are marked with a 💔.