QObject signal/slot connection syntax redundancies

  • Is it just me, or is the connection syntax unnecessarily verbose? Considering Qt heavily relies on generated code, how hard would it be to trim the connect syntax down to the very essentials, or something like:

    connect(someObject.someSignal(), someOtherObject.someSlot());@

    The moc should be perfectly capable of getting the types, addresses and correct signal/slot overloads and generate a type safe connection syntax.

  • Moderators

    I always had a similar feeling. I don't know the guts of MOC, but from a point of view of an ignorant, it does seem that the syntax you propose should be just as easy to implement as the current one (Qt4 style, that is).

  • Yes, while the Qt4 syntax is a little easier it still has the same redundancies, just in an easier to type form. But with Qt5 things really went bad, especially in the occasional case of having to help resolve overloads ambiguities.

    it just seems that generating the actual signature from the "example above" is a very easy thing to do, and considering that we live in the information age, where simple, tedious and repetitive tasks should be offloaded to a machine, I am really puzzled not only is this still done manually but getting even more tedious.

    Maybe I am missing something, who knows...

  • Moderators

    Let me be the counterweight here :)
    There are currently 2 valid choices for the connect syntax:
    @connect(object, SIGNAL(signal(type)), object2, SLOT(slot(type)))@
    This syntax is a bit dated and clearly indicates the use of macros and this sort of trickery. Just by looking at it you can tell there's something more going on under the covers as signal(type) is not even a valid C++ syntax (it becomes a string after a macro expansion).
    @connect(object, &ObjectType::member, object2, &ObjectType2::member)@
    I actually really like this syntax, as it is free of C'ish macros and looks like a modern C++ code. It clearly hints that there are function addresses involved and there is no executing them here.

    Now there's the syntax you propose:
    @connect(someObject.someSignal(), someOtherObject.someSlot());@
    Now this would worry me. You can't tell by just a look that someObject.someSignal() is not a CALL to a member! Of course moc could turn it into anything, but readability drops way down under!
    Somebody not Qt familiar would look at it and said "oh, a call to function someSignal and someSlot, and the results passed as parameters to connect". Wrong! that's not at all what is going on and there's no indication whatsoever that some sort of function and object pointers are stored to invoke them at a later date.

    I feel it's the same path that Microsoft went with the C++/CX - usage of ^ instead of proper C++ reference counting classes.

    Tool generated code shortens the syntax, no argument there, but can also be a burden, especially for newcomers.
    I personally think that magically generated code should be kept to minimum and with the advances that C++ made in the last few years the benefit of such should be carefully measured. There a lot of benefit to using moc for generation of the qt meta-code, since it would be a tedious thing to do it manually for every QObject based class, but in case of connect there's little to gain and a lot of clarity can easily be lost.
    If moc should touch connects, it better clearly indicate that with the syntax, like the damned /CX hat :P

  • @ connect(&object, &ObjectType::member, &object2, &ObjectType2::member)@
    ^fixed that one for you

    This is a lot of unnecessary typing of a class and member plus :: and address operators, and god forbid - resolving overloads. And without the parameters you have to do ridiculous casting in order to specify the overload manually.

    The idea is to provide all that data with the minimum set of parameters, which are which instance of the type and which of the class members need to be connected, and in the moc substitute that with the identifier/address of and & + type + :: + member.

    I think the current concept as it is is actually more confusing, because signals are member methods but they are not called but emitted. Also, in the connection you pass the address of the class member function , which is static, not on a per instance basis, but signals are entirely encapsulated within their object instances.

    As of which is more or less readable, this is a matter of personal opinion and habits which tend to bias. It is amazing what kind of mental masochism people can become used to and eventually insist upon and become defensively-offensive about :)

    I think my suggestion is by far the quickest and most intuitive though, as long as you consider the fact you are making a connection. If you are a newbie and this is the way you learn it and able to do it, then you are saved a bit of pain in your life. Plus if I can clearly express my intent in 7 tokens, why use 12?

    I am not a big fan of generated code either. That is one of the things I mostly dislike about Qt. But... if there is to be significant amount of it, especially when using declarative, then why the hell not go all the way.

    But nevertheless, all programming languages boil down to code generation, even if you write in assembly it is still translated to machine binary. If you want to "KEEP IT REAL" and binary you could write an application or two in your lifetime... if you are an autistic savant capable of memorizing arbitrary binary numbers in varying bit-widths.

  • Moderators

    Actually my code was fine as it was, but thanks, I should've named it objectPtr I guess.
    Get it? My name confused you because it didn't properly reflect what it was and you're not a newbie from what I can tell. Readability matters ;)

    You go on to make those exaggerations. Who said anything about assembly or keeping it real?? I'm not saying the existing one is the best and shortest syntax possible and I'm really an eager adopter of changes. I certainly don't have any devotion or anything like that and the overloaded methods syntax is definitely not pretty.
    I'm merely pointing out that code generators are not the way to go in my opinion. If anything, the core language should evolve to reflect those needs, like it did with auto, lambdas and so many other things.

    I'll stick to the readability argument if you don't mind. Again, I'm not saying what we have can't be simplified. I don't have an idea how to do it at hand, but in my opinion your proposal is just too misleading. Whenever I see whatever() I think "function call". Maybe you could drop the parentheses, maybe you could do something else, I don't know. Whatever it would be I'm just saying it should tell me what it does and if possible - with the existing language syntax to ease the learning curve and lower the tooling maintenance burden.
    Take a look at these forums. A lot of people have hard time grasping the concept of moc and uic as it is, and there was a lot of effort to make it as invisible to the user as possible - hiding it behind macros and what not. Now you propose to introduce function calls that are not really function calls.

    Another thing to mention is that your syntax would probably confuse the hell out of some static code analyzers out there. You'll probably say that those tools can be "upgraded" to understand Qt too, but lets make a reality check before we go on and do it ;)

  • Well, without any signal parameters you probably don't need the () - and if you need to specify an overload, then the () will have to make a comeback in a much uglier (albeit "modern C++" code) form. Not to mention signals and slots are not a standard C++ feature, and even if they one day become, those would most likely be boost signals, so I don't see much benefit in keeping a non-c++ feature conformed to C++ styling code, especially when that makes the syntax more awkward.

  • Moderators

    Ok, let me try to be more constructive. A lot can be done without extending the syntax. Consider this trick:

    template<class T> struct unref { typedef T type; };
    template<class T> struct unref<T&> { typedef T type; };
    #define CONNECT(sender, signal, receiver, slot)
    connect(sender, &unref<decltype(*sender)>::type::signal,
    receiver, &unref<decltype(*receiver)>::type::slot)
    Now you can use even shorter syntax:
    CONNECT(senderPtr, signal, receiverPtr, slot);
    It's conforming c++, will work with external tools, give proper error messages when you mix up types and doesn't lie about what it's doing.
    Of course this has limitations, but it's just a tiny example of how powerful "naked" c++ is.
    I'm just saying that if there's a way to express some feature in the native syntax we should go for it. If a few extra ampersands really hurt your eyes that much you can alwas cover those up with macros that are still valid c++.

Log in to reply