Я обнаружил, что boost :: signal2 использует своего рода ленивое удаление подключенных слотов, что затрудняет использование соединений как нечто, управляющее временем жизни объектов. Я ищу способ принудительно удалить слоты при отключении. Любые идеи о том, как обойти эту проблему, разрабатывая мой код по-другому, также приветствуются!
Это мой сценарий: у меня есть класс Command, отвечающий за выполнение чего-то, что требует асинхронного времени и выглядит примерно так (упрощенно):
class ActualWorker {
public:
boost::signals2<void ()> OnWorkComplete;
};
class Command : boost::enable_shared_from_this<Command> {
public:
...
void Execute() {
m_WorkerConnection = m_MyWorker.OnWorkDone.connect(boost::bind(&Command::Handle_OnWorkComplete, shared_from_this());
// launch asynchronous work here and return
}
boost::signals2<void ()> OnComplete;
private:
void Handle_OnWorkComplete() {
// get a shared_ptr to ourselves to make sure that we live through
// this function but don't keep ourselves alive if an exception occurs.
shared_ptr<Command> me = shared_from_this();
// Disconnect from the signal, ideally deleting the slot object
m_WorkerConnection.disconnect();
OnComplete();
// the shared_ptr now goes out of scope, ideally deleting this
}
ActualWorker m_MyWorker;
boost::signals2::connection m_WorkerConnection;
};
Класс вызывается примерно так:
...
boost::shared_ptr<Command> cmd(new Command);
cmd->OnComplete.connect( foo );
cmd->Execute();
// now go do something else, forget all about the cmd variable etcetera.
Класс Command поддерживает себя, передавая себе shared_ptr, который связан с сигналом ActualWorker с помощью boost :: bind.
Когда работник завершает работу, вызывается обработчик в Command. Теперь, поскольку я хотел бы, чтобы объект Command был уничтожен, я отключаюсь от сигнала, как видно из кода выше. Проблема заключается в том, что фактический объект слота не удаляется при отключении, он только помечается как недействительный, а затем удаляется позднее. Это, в свою очередь, зависит от того, будет ли сигнал снова срабатывать, чего в моем случае не происходит, что приводит к тому, что время прорезания никогда не истекает. Таким образом, объект boost :: bind никогда не выходит из области видимости, удерживая shared_ptr в моем объекте, который никогда не будет удален.
Я могу обойти это, связывая указатель this вместо shared_ptr, а затем поддерживая мой объект живым, используя член shared_ptr, который я затем освобождаю в функции обработчика, но это отчасти заставляет дизайн чувствовать себя немного слишком сложным. Есть ли способ заставить сигналы2 удалить слот при отключении? Или есть что-то еще, что я мог бы сделать, чтобы упростить дизайн?
Любые комментарии приветствуются!