Рассмотрим следующую ситуацию:
struct A {
// `unique_function` is like std::function but does not
// require its target to be Copyable, and is in turn
// itself not Copyable either.
unique_function<void()> callback = []{};
void set_callback(unique_function<void()> new_callback) {
callback = std::move(new_callback);
callback();
}
};
int main() {
A a;
a.set_callback([&, x = std::make_unique<int>(42)]{
std::cout << "Ok at this point, *x = " << *x << ".\n";
a.set_callback([]{}); // This destroys enclosing lambda
// while is is being executed, thus messing things up.
// Captures are not legal to access here.
});
}
Предполагаемая семантика в этой ситуации состоит в том, чтобы все обратные вызовы вызывались, когда они установлены, и последний установленный набор сохранялся в callback
когда вершина set_callback
возвращается.
Решения, которые я вижу:
замещать unique_function<void()>
с чем-то копируемым, как std::shared_ptr<unique_function<void()>>
или специализированный shared_function<void()>
а затем вызвать копию:
shared_function<void()> callback = []{};
void set_callback(shared_function<void()> new_callback) {
callback = std::move(new_callback);
auto callback_copy = callback;
callback_copy();
}
Недостатки:
Иметь список и добавлять к нему обратные вызовы перед их выполнением. Когда вершина set_callback
возвращается, callbacks.back()
является последним установленным обратным вызовом, а остальные могут быть удалены. Для этого требуется дополнительный счетчик, отслеживающий текущую глубину set_callback
звонки.
std::deque<unique_function<void()>> callbacks;
std::size_t depth = 0;
struct depth_controller {
std::size_t& depth;
depth_controller(std::size_t& d) : depth(d) { ++depth; }
~depth_controller() { --depth; }
};
void set_callback(shared_function<void()> new_callback) {
callbacks.emplace_back(std::move(new_callback));
{
depth_controller dctl{depth};
callbacks.back()();
}
if (depth > 0) { return; }
callbacks.erase(callbacks.begin(),
std::prev(callbacks.end()));
}
Недостатки:
Можем ли мы сделать лучше?
Задача ещё не решена.
Других решений пока нет …