Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are signals and slots better than plain old callbacks?

Newbie to C++ here. I was reading A Deeper Look at Signals and Slots, which claims that 1) callbacks are inherently type-unsafe, and 2) to make them safe you need to define a pure virtual class wrapper around your function. I'm having a hard time understanding why that's true. As an example, here is the code Qt provides on their tutorial page for signals and slots:

// Header file
#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;
};

// .cpp file
void Counter::setValue(int value)
{
    if (value != m_value) {
        m_value = value;
        emit valueChanged(value);
    }
}

// Later on...
Counter a, b;
QObject::connect(&a, SIGNAL(valueChanged(int)),
                 &b, SLOT(setValue(int)));

a.setValue(12);     // a.value() == 12, b.value() == 12
b.setValue(48);     // a.value() == 12, b.value() == 48

Here is that code rewritten using callbacks:

#include <functional>
#include <vector>

class Counter
{
public:
    Counter() { m_value = 0; }

    int value() const { return m_value; }
    std::vector<std::function<void(int)>> valueChanged;

    void setValue(int value);

private:
    int m_value;
};

void Counter::setValue(int value)
{
    if (value != m_value) {
        m_value = value;
        for (auto func : valueChanged) {
            func(value);
        }
    }
}

// Later on...
Counter a, b;
auto lambda = [&](int value) { b.setValue(value); };
a.valueChanged.push_back(lambda);

a.setValue(12);
b.setValue(48);

As you can see, the callback version is type-safe and shorter than the Qt version, despite them claiming that it's not. It does not define any new classes, aside from Counter. It uses only standard library code and doesn't need a special compiler (moc) to work. Why, then, are signals and slots preferred over callbacks? Has C++11 simply obsoleted these concepts?

Thanks.

like image 781
James Ko Avatar asked Dec 12 '15 06:12

James Ko


People also ask

Why do we need signals and slots when we can directly call 2 objects using their member functions?

A public slots section contains slots that anyone can connect signals to. This is very useful for component programming: you create objects that know nothing about each other, connect their signals and slots so that information is passed correctly, and, like a model railway, turn it on and leave it running.

How do signals and slots work?

In Qt, we have an alternative to the callback technique: We use signals and slots. A signal is emitted when a particular event occurs. Qt's widgets have many predefined signals, but we can always subclass widgets to add our own signals to them. A slot is a function that is called in response to a particular signal.

Are signals and slots thread safe?

It is generally unsafe to provide slots in your QThread subclass, unless you protect the member variables with a mutex. On the other hand, you can safely emit signals from your QThread::run() implementation, because signal emission is thread-safe.


2 Answers

There's one enormous difference between the two: threads.

Traditional callbacks are always called in the context of the calling thread. Not so with signals and slots -- as long as the thread is running an event loop (as it will be if it's a QThread) the slot can be in any thread.

Sure, you can do all of this manually with a callback -- I've written many Win32 apps over the years that use Windows-style message pumps that juggle callbacks across threads -- but it's a lot of boilerplate code and not much fun to write, maintain, or debug.

like image 131
MrEricSir Avatar answered Sep 29 '22 08:09

MrEricSir


In general: Signals and Slots differs from callbacks by the fact that it decouples the call (Signal) from the Handler (Slot). Which means: you can register your slot to be on a different thread, you can listen to one signal from many slots and change queuing strategy easily. but it has its costs(In QT world at least...): string evaluation and generally more internal work / code branches.. in short, it's an higher level concept.

that being said, you can do all of those with simple callbacks, but it will be like reinventing the wheel.

like image 38
guy Avatar answered Sep 29 '22 07:09

guy