13

In my one year of Qt programming, I have learnt a lot about signals and slots. But not enough...

http://doc.qt.io/qt-5/signalsandslots.html

Slots can be used for receiving signals, but they are also normal member functions.

So... Is there any reason not to declare every single function in a class that inherits from QObject, as a slot, whether it needs to be one or not ?

In the link above, they give an example:

A small QObject-based class might read:

#include <QObject>

class Counter : public QObject
{
    Q_OBJECT

public:
    Counter() { m_value = 0; }

    int value() const { return m_value; }

public slots:
    void setValue(int value);

signals:
    void valueChanged(int newValue);

private:
    int m_value;
};

Why define the value() function as a plain function and not a slot ? Would there be any negative results if they did make it a slot ?

Thalia
  • 13,637
  • 22
  • 96
  • 190

5 Answers5

19

Back in the olden days, you had no choice but to use slots if you want to connect to signals. This is no longer the case in Qt 5, where connections can be made to regular member functions or even free functions or lambdas.

Declaring a slot registers that function into that particular object's meta data, making it readily available to all of Qt's functionality which relies on the meta object. Other than that, it is a regular member function as the documentation states. There is nothing special in the slot function itself, the difference is in the generation of meta data for it in the meta object.

This means that declaring slots comes at some cost, albeit a small one, both in compilation time and executable size. I'd say it is overkill to have all your public functions as slots. It would be more efficient to only use slots when you actually need slots, if it can't work with a regular function, make it a slot.

Also, note that in almost all of the cases, signals are declared with a return type void. This has to do with the typical usage case of signals - they can often pass a parameter, but rarely return anything. Even though it is possible to return a value through a signal connected to a slot, this is used extremely rarely. So it doesn't make a lot of sense to declare a function that returns something as a slot you intend to connect to, as the very fact it returns a value means it will most likely not be used in the typical signal/slot context. That's why the getter is not a slot in that example. The setter as a slot is redundant in Qt 5, and is probably the product of this example code dating back to Qt 4.

Lastly, separating slots from regular public functions is a good way to illustrate intent, or the "API" of that class if you will. For example, I mostly use slots when extending QML, so I don't have to mark every function explicitly as invokable - unlike the scenario mentioned in the above paragraph, such slots often return stuff, but they aren't really used in connections. This way I get a clear overview of the design of the interface the class provides.

dtech
  • 47,916
  • 17
  • 112
  • 190
2

In addition to ddriver's answer, which is the best / correct answer (+1 there), I would also argue that it is confusing to define all member functions as public slots. They way you define functions (private / public / slots etc...) has an effect on the perceived usage of the class.

What I mean by that is.... you could argue that all functions should just be public (or public slots) and then this covers all cases. However this could be confusing to a future user of your class. Consider that int value() is a public slot (its not the best example) someone could try to use it as one, but the function itself has a return value which does not really make sense for a slot. It does make sense for a normal member function where (as a normal function call) the return value can be accessed.

One rule to go by is to always keep your functions and variables as local scoped and private as you can (by default) and only open them up for other use (public-ness, slots, global, etc...) when you really need them. This keeps your class interface much easier to understand and avoid confusion for later users.

I am sure there is a name for this rule-of-thumb so you can look it up on some coding technique site, but I can't recall it :(

edit

Another minor example is the auto complete... if all your functions are slots, then when you are doing your connect(this, myClass::mySignal, &someOtherClass, SomeOtherClass::<auto complete options> ); your list of auto complete options could be long and unclear what is what. If you just have a few specific members that are slots then its easier to know which to choose.

code_fodder
  • 15,263
  • 17
  • 90
  • 167
  • 1
    The term you were looking for is encapsulation. – thuga Mar 09 '16 at 07:36
  • @thuga - Yes encapsulation definitely is the subject... but I am sure there some "rule name" for keeping everything minimally scoped.... maybe its just the "rules of encapsulation"? :o thanks : ) – code_fodder Mar 09 '16 at 07:39
  • The only thing I'd add to this answer is that public slots are Q_INVOKABLE, and you may not want all your public methods to be accessible from a QML context. – MrEricSir Mar 09 '16 at 18:32
2

In reference to encapsulation from code_fodder's answer, it should be noted that there's really no such thing as a private slot.

For example:

class MyClass : public QObject
{
    Q_OBJECT
public:
    MyClass()
        :QObject(NULL) {}

private slots:
    void Hello() { qDebug("Hello World\n"); }
};

#include "main.moc"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    MyClass* cls = new MyClass;
    QMetaObject::invokeMethod(cls, "Hello");

    return a.exec();
}

As we can see here, calling the functionHellois successful from outside the class.

TheDarkKnight
  • 27,181
  • 6
  • 55
  • 85
  • Does it work for connections too? BTW the responsibility for the possibility to invoke private functions is entirely on Qt, there is no access checking. The private slots are very real though, even if not respected by the meta system :) – dtech Mar 09 '16 at 13:20
  • Yes, if you connect via the old connection method (pre Qt 5), using the SIGNAL, SLOT macros. For example with a QTimer *connect(pTimer, SIGNAL(timeout()), cls, SLOT(Hello()) );* – TheDarkKnight Mar 09 '16 at 13:29
  • 1
    @TheDarkKnight its a really interesting observation (+1 for sharing). I always use private slots when setting up internal connection within the same class and public otherwise.... but I never actually thought to check (or to notice) that Qt does not inhibit visibility of the private slot! - still, this won't change the way I define them because even if Qt is lenient (or just wrong here) I don't like to be : ) – code_fodder Mar 09 '16 at 18:52
  • 1
    I was aware of this (odd) behavior - I found this info in the documentation - which is why I specifically asked about `public` functions and slots. Still thank you for adding this info. – Thalia Mar 09 '16 at 23:04
  • @code_fodder Like you, I also declare private slots. At least that way, the interface shows what should, by design, be kept private, even if it is still publicly accessible. – TheDarkKnight Mar 10 '16 at 10:09
  • @Thalia, I expect the reason behind this is that using the macros determines the connection at run time, so encapsulation at that point is irrelevant. In contrast, the Qt 5 connection provides compilation verification and causes an error if the target slot is private. – TheDarkKnight Mar 10 '16 at 10:12
0

Another use case where you possibly don't want all methods to be slots is when you are exposing objects to JavaScript through Qt's WebKit Bridge: all public slots are invocable from the JavaScript and that might open up security issues, depending on whether the JavaScript can be trusted or not.

So if you want a public method which is not invocable from JavaScript, you may not declare it as a slot.

Ignitor
  • 2,907
  • 33
  • 50
  • The major reason for my question was because I have to expose objects to JavaScript - and the people who use that particular aspect of my application are very happy to request that pretty much every function can be a slot. I am trying to limit this... but I needed a reason why. For functions that return a value, the functions can become invokable from JavaScript by making them `Q_PROPERTY`. – Thalia Mar 09 '16 at 23:08
-1

For certain functions, you need a return value. This will not work as easily in slots. In slots you cant use the return value of a function or give a reference parameter to them. Yes you can do it, but you have a timing Problem. Whether you're using a slot or a normal member function dependents on your software architecture.

In addition, slots run in the event loop. It depends on your code if this is intentional or not.

  • 1
    This answer doesn't make the slightest bit of sense, and doesn't seem to even address the question. – MrEricSir Mar 09 '16 at 01:25
  • Can you use return values in slots? Or even get the value of a referenced parameter in the correct way? – SuperFliege Mar 09 '16 at 09:06
  • 1
    @SuperFliege - the slot is just a function, it would work exactly the same way as a function. Multithreading and object lifetime are a completely different matter, and affect regular functions just as much as they affect slots. – dtech Mar 09 '16 at 12:40