How do I forward a signal to private signal?

Viewed 1413

Suppose I have the following class:

class Example : public QObject {
    Q_OBJECT
public:
    explicit Example(QObject * parent = nullptr);
    void example();

signals:
    void publicSignal();
    void privateSignal(QPrivateSignal);
};

The privateSignals use a QPrivateSignal and therefore can only get emitted by the respective classes. Since the moc compiler removes the QPrivateSignal from the signature, I can use connect as usual:

Example * foo = new Example(this);
Example * bar = new Example(this);

connect(foo, &Example::publicSignal,  bar, &Example::example); // works fine, signatures fit
connect(foo, &Example::privateSignal, bar, &Example::example); // works fine, QPrivateSignal not part of signal

I can easily forward both signal variants to the publicSignal variant:

connect(foo, &Example::publicSignal,  bar, &Example::publicSignal); // forward foo's signals to bar
connect(foo, &Example::privateSignal, bar, &Example::publicSignal); // forward foo's private signals to bar

That's apparently not possible with private signals, even within Example's constructor:

Example::Example(QObject * parent) : QObject(this) {
    // Suppose that SimilarExampleClass looks the same but does not recurse
    SimilarExampleClass * other = new SimilarExampleClass(this);

    // this does not compile either
    connect(other, &SimilarExampleClass::publicSignal, this, &Example::privateSignal);
}

Both instances lead to the following errors:

$QTHOME/include/QtCore/qobject.h: In instantiation of ‘static QMetaObject::Connection QObject::connect(const typename QtPrivate::FunctionPointer<Func>::Object*, Func1, const typename QtPrivate::FunctionPointer<Func2>::Object*, Func2, Qt::ConnectionType) [with Func1 = void (SimilarExampleClass::*)(); Func2 = void (Example::*)(Example::QPrivateSignal); typename QtPrivate::FunctionPointer<Func>::Object = SimilarExampleClass; typename QtPrivate::FunctionPointer<Func2>::Object = Example]’:
example.cpp:107:89:   required from here
$QTHOME/include/QtCore/qobject.h:239:9: error: static assertion failed: The slot requires more arguments than the signal provides.
         Q_STATIC_ASSERT_X(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
         ^
$QTHOME/include/QtCore/qobject.h:241:9: error: static assertion failed: Signal and slot arguments are not compatible.
         Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),
         ^
...

$QTHOME/include/QtCore/qobject.h: In instantiation of ‘static QMetaObject::Connection QObject::connect(const typename QtPrivate::FunctionPointer<Func>::Object*, Func1, const typename QtPrivate::FunctionPointer<Func2>::Object*, Func2, Qt::ConnectionType) [with Func1 = void (SimilarExampleClass::*)(SimilarExampleClass::QPrivateSignal); Func2 = void (Example::*)(Example::QPrivateSignal); typename QtPrivate::FunctionPointer<Func>::Object = SimilarExampleClass; typename QtPrivate::FunctionPointer<Func2>::Object = Example]’:
example.cpp:108:90:   required from here
$QTHOME/include/QtCore/qobject.h:241:9: error: static assertion failed: Signal and slot arguments are not compatible.
         Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),
         ^

Is there any way I can use a private signal as a destination for connect within the signal's class?

2 Answers

Unfortunately not without a small helper. Qt's automatic QPrivateSignal filter from signals in its connect syntax does not port over to its slot argument. Therefore, you need a lambda for your Example:

connect(other, &SimilarExampleClass::publicSignal, this, [this](){
    emit privateSignal({});
});

The old school signal/slot form does work with private signals:

connect(other, SIGNAL(publicSignal()), this, SIGNAL(privateSignal()));

I stumbled across this problem when trying to connect to QTimer's signal timeout, which is a private signal at least since Qt5.9

Related