首页 > 代码库 > Quest for sane signals in Qt - step 1 (hand coding a Q_OBJECT)
Quest for sane signals in Qt - step 1 (hand coding a Q_OBJECT)
探索qt的信号
ref: http://crazyeddiecpp.blogspot.hk/2011/01/quest-for-sane-signals-in-qt-step-1.html
If it wasn‘t for the particular implementation of signals that Qt has, it would be a quite wonderful library. So much about it has been made very easy in comparison to most other UI libraries for C++. I really enjoy working with the library except for its signal architecture. Don‘t get me wrong, the signal/slot idea is a vast improvement over every other thing out there; what I don‘t like is Qt‘s way of doing it.
The way I see it, there‘s two fundamental problems with Qt‘s approach:
qt的信号机制有两个根本问题
1) If I wanted Python I‘d use it. C++ is a strongly typed language and this feature is extremely powerful in that it provides much opportunity to catch bugs before they ever happen. The designers of Qt apparently don‘t like this feature though and spent a lot of time and effort making QObjects dynamic types. You can connect slots to signals without knowing whether or not either one is actually real. The problem with this is that you don‘t know that you‘ve spelled something wrong until you run the program and happen upon the problem; this can be damn difficult to debug in some cases too, which is one good reason why I‘m NOT using Python.
没有了强类型
2) Qt slots can only exist in Q_OBJECT classes. This has more implications than simply being bound to QObject because Q_OBJECT classes must be processed by the moc. The moc, in turn, implements an incomplete C++ preprocessor and is not compatible with template classes. Since only slots can be connected to signals you‘re stuck having to implement a hand-coded Q_OBJECT for each one. This means you can‘t auto-generate slot classes. Furthermore, since slots have to be complete objects and not just functors as with other C++ implementations of the signal/slot architecture, you can‘t connect to binds or lambda expressions.
你不能自动生成槽类
您无法连接到绑定或lambda表达式
The first of these is frustrating and slows me down, but the second is actually fairly debilitating compared to other signal/slot implementations. The really great thing about implementations like boost::signals (or signals2 - even better) is that you can connect signals to objects that don‘t even know anything about signals or slots. This means that your model and data classes don‘t have to be bound to this kind of use and you don‘t need to hand-code in-between objects to connect them (as you do in Qt). In fact, these mechanisms allow you to even connect to free functions, giving you even more possibilities for expression without creating new, pointless coupling.
However, like I said, the rest of the Qt library is great and since the developers actually cared about commercial development on Windows operating systems, it makes the single best C++ option that exists for UI development on that system. Even though it is "cross-platform" it is still the best for native development that you never intend to use anywhere else. The simple fact is that native options, such as MFC, are horrible to use. The GTKmm library uses something similar to boost::signals but unfortunately doesn‘t provide accessibility on Windows, unlike Qt. This feature is absolutely needed by anyone who intends to auto-drive their UI through testing scripts on that system since all such test suites use the accessibility framework to do their thing.
但是qt支持ui测试啊
So, given that I hate the Qt signal system but need to use Qt, my quest is to find a method to sanitize Qt‘s signals. I don‘t intend to "fix" them, they‘ll still be there and being used, but I do intend to fix the interface to them. I want static typing back and I want to be able to connect to any function like interface, just like boost::signals. Thus the real goal here is to turn a Qt signal into a boost signal. We don‘t have to go the other way around because Qt slots are just functions (with some junk about them in some meta-information) so we can connect them to any boost signal freely.
所以虽然我很恨他的slot机制,但也必须用他
我想搞掉slot
It would be nice to be able to do this on the fly. So I‘m looking for a use case something like so:
我想实现如下功能:
connect_static(qt_object, qt_signal, [](args){ ... });
Of course the problem I have at this point is that whatever does the translation MUST be a Q_OBJECT. Of course it can‘t be because I need to be able to generate the translation functor from the parameter description. The only ways to do that is of course to extend the moc in some way, use the preprocessor, or (my preferred choice) template meta-programming. The first is simply not practical and the later two are incompatible with the moc. Thus, what we need to do here is create a Q_OBJECT without using the moc. Today I pulled this off and it actually turned out to be a LOT easier than looking at the source code and various web sites would indicate.
The first thing I did was to examine the source code that is generated by the moc and try to figure it out. I actually made it pretty far but it was obvious that creating preprocessor macros (didn‘t think TMP would do it) to mimic the MOC would be damn daunting. Here‘s one website that describes the Qt metamodel:
Qt Internals & Reversing
I also tried to ask on stackoverflow and qtcentre for help to see if all of this was even necessary. The former generated few responses indicating the MOC and any of my ideas to do this where incompatible. The latter just generated a flame war in response to my frustration with a whole lot of very silly answers, such as telling me to put a particular class definition in "#ifdef" blocks (not a good place to go to for help, lots of really poor advice being handed out at that site). I did, however, eventually yank out a link from one individual that actually ended up holding what I believe is the key to getting this accomplished:
Dynamic Signals and Slots
That site alone does not contain exactly what I need, but pulling various bits from it and adding bits I learned from looking at what qt_metacall does (stepped all the way through it quite a few times trying to figure out why my slots where not being called) resulted in the ability to create a QObject derived class that responds to any signal attached to it and could then be extended to forward to a subclass, which would not have to be a Q_OBJECT:
我从这个网站学到如何写一个class能反映任何连接到她的signal,然后forward到子类,而该子类不必是qobject
struct listener2 : public QObject
{
int qt_metacall(QMetaObject::Call call, int id,void** args)
{
id = QObject::qt_metacall(call,id,args);
if (id == -1 || call != QMetaObject::InvokeMetaMethod)
return id;
assert(id == 42);
std::cout << "Funky slot!!" << std::endl;
return -1;
}
void make_connection(QObject * obj, char const* signal)
{
QByteArray sig = QMetaObject::normalizedSignature(signal);
int sigid = obj->metaObject()->indexOfSignal(sig);
QMetaObject::connect(obj, sigid, this, QObject::metaObject()->methodCount() + 42);
}
};
This object must be connected with "make_connection" rather than with connect, because otherwise the system breaks down when it tries to find the slot and doesn‘t, but besides that it is almost perfect. The args void* array actually points at the arguments generated by the signal and then must be reinterpret_cast to the appropriate thing (see the .moc output of any of your classes).
该object必须使用"make_connection"链接,否则系统会尝试着slot但找不到。除此之外他是完美的。参数void*指向信号产生的参数,他必须reinterpret_cast到合适的类型
The key here is to find the id of the signal you want to connect to and use QMetaObject::connect instead, supplying an id you can recognize on the other side. You must add the methodCount() of your base so that it can take care of existing slots. In the qt_metacall function you first let the base attempt to take care of it. This will subtract from the input id resulting in id‘s that YOUR derived object is meant to take care of. The assert is unnecessary but adds some semblance of safety. The return of -1 in standard Qt speak for, "I‘ve handled this call."
这里的关键是找到你想连接的信号的id,然后使用QMetaObject::connect链接 (mm的,俺要学习一下具体是怎么回事)
This is a big step. A subclass to this thing could take the void** data and then use TMP to generate the correct casts and invoke a boost::signal. Implementing that will be the subject of "step 2" followed by attempts to regain static typing and safety.
POSTED BY CRAZY EDDIE AT 3:55 PM
1 COMMENT:
user117January 5, 2011 at 2:08 PM
Don‘t you think this Qt‘s explanation is reasonable:
http://doc.qt.nokia.com/4.7/templates.html
?
Quest for sane signals in Qt - step 1 (hand coding a Q_OBJECT)