Here are two approaches to hide the bookkeeping issues.
First, we maintain a std::vector
which, on destruction, disconnects us from the source:
typedef std::shared_ptr<void> listen_token;
struct disconnecter {
QMetaObject::Connection conn;
disconnecter( QMetaObject::Connection&& c ):conn(std::move(c)) {}
~disconnecter() { QObject::disconnect(conn); }
};
template<class F, class T, class M>
listen_token QtConnect( T* source, M* method, F&& f ) {
return std::make_shared<disconnecter>(
QObject::connect( source, method, std::forward<F>(f));
);
}
typedef std::vector<listen_token> connections;
Then we connect as follows:
connections conns;
conns.emplace_back( QtConnect( bob, &Bob::mySignal, [](QString str){ std::cout << "Hello World!\n"; } ) );
when the vector is destroyed, the connection objects are also destroyed.
This is similar to how other signal/slot systems are handled, where the listener keeps track of a token, then returns it. But here, I keep the disconnection object in an opaque type that cleans up the connection on destruction.
Note that copying that vector will extend the lifetime of the connection. If the message is going to a particular instance of a class, store a connections
instance in the class, and you won't get messages after the instance is destroyed.
A second approach, based off what @lpapp found, is if you have a lambda that you want to call only once in response to a signal, then disconnect:
template<class F>
struct auto_disconnect_t {
F f;
std::shared_ptr<QMetaObject::Connection> conn;
template<class U>
auto_disconnect_t(U&& u):
f(std::forward<U>(u)),
conn(std::make_shared<QMetaObject::Connection>())
{}
template<class... Args>
void operator()(Args&&... args)const{
QObject::disconnect(*conn);
f( std::forward<Args>(args)... );
}
};
template<class T, class M, class F>
void one_shot_connect( T* t, M* m, F&& f ) {
typedef typename std::decay<F>::type X;
auto_disconnect_t<X> helper(std::forward<F>(f));
*helper.conn = QObject::connect( t, m, helper );
};
here we one_shot_connect( bob, &Bob::mySignal, [](QString str) { std::cout << "Hello\n" } );
, and the next time the signal fires we get the message, and then the connection is disconnected.
I disconnect before processing your lambda, just in case the lambda causes the signal to fire or something.