假设你有一个包含某种可调用对象集合的类foo. foo有一个成员函数run(),它遍历集合并调用每个函数对象. foo还有一个成员remove(…),它将从集合中删除一个可调用对象.
是否有一个惯用的,RAII风格的后卫,你可以放入foo.run()和foo.remove(…),以便通过调用foo.run()驱动删除
将被推迟到守卫的破坏者开火?它可以用标准库中的东西来完成吗?这个模式有名字吗?
我目前的代码似乎不够优雅,所以我正在寻找最佳实践类型的解决方案.
注意:这不是关于并发性的.非线程安全的解决方案很好.问题在于重新引入和自我引用.
这是一个问题的例子,没有优雅的“延迟删除”警卫.
- class ActionPlayer
- {
- private:
- std::vector<std::pair<int,std::function<void()>>> actions_;
- public:
- void addAction(int id,const std::function<void()>& action)
- {
- actions_.push_back({ id,action });
- }
- void removeAction(int id)
- {
- actions_.erase(
- std::remove_if(
- actions_.begin(),actions_.end(),[id](auto& p) { return p.first == id; }
- ),actions_.end()
- );
- }
- void run()
- {
- for (auto& item : actions_) {
- item.second();
- }
- }
- };
其他地方:
- ...
- ActionPlayer player;
- player.addAction(1,[]() {
- std::cout << "Hello there" << std::endl;
- });
- player.addAction(42,[&player]() {
- std::cout << "foobar" << std::endl;
- player.removeAction(1);
- });
- player.run(); // boom
编辑…好吧,这是我通过RAII锁定对象看到的方式.假设递归最终终止(如果不是用户的错误),以下应该处理在运行中运行的抛出和重入调用的操作.我使用了缓存的std :: functions,因为在这段代码的实际版本中,addAction和removeAction的等价物是模板函数,它们只能存储在一个vanilla homogeneously类型的容器中.
- class ActionPlayer
- {
- private:
- std::vector<std::pair<int,std::function<void()>>> actions_;
- int run_lock_count_;
- std::vector<std::function<void()>> deferred_ops_;
- class RunLock
- {
- private:
- ActionPlayer* parent_;
- public:
- RunLock(ActionPlayer* parent) : parent_(parent) { (parent_->run_lock_count_)++; }
- ~RunLock()
- {
- if (--parent_->run_lock_count_ == 0) {
- while (!parent_->deferred_ops_.empty()) {
- auto do_deferred_op = parent_->deferred_ops_.back();
- parent_->deferred_ops_.pop_back();
- do_deferred_op();
- }
- }
- }
- };
- bool isFiring() const
- {
- return run_lock_count_ > 0;
- }
- public:
- ActionPlayer() : run_lock_count_(0)
- {
- }
- void addAction(int id,const std::function<void()>& action)
- {
- if (!isFiring()) {
- actions_.push_back({ id,action });
- } else {
- deferred_ops_.push_back(
- [&]() {
- addAction(id,action);
- }
- );
- }
- }
- void removeAction(int id)
- {
- if (!isFiring()) {
- actions_.erase(
- std::remove_if(
- actions_.begin(),[id](auto& p) { return p.first == id; }
- ),actions_.end()
- );
- } else {
- deferred_ops_.push_back(
- [&]() {
- removeAction(id);
- }
- );
- }
- }
- void run()
- {
- RunLock lock(this);
- for (auto& item : actions_) {
- item.second();
- }
- }
- };